aboutsummaryrefslogtreecommitdiffstatshomepage
path: root/tunnel
diff options
context:
space:
mode:
authorJason A. Donenfeld <Jason@zx2c4.com>2019-05-20 14:18:01 +0200
committerJason A. Donenfeld <Jason@zx2c4.com>2019-05-20 14:18:01 +0200
commite493f911269a2dabab7b05ec28726cdaeffb660e (patch)
treedb88ec568dfc508da863e67164de909448c66742 /tunnel
parentservice: move route monitor and account for changing index (diff)
downloadwireguard-windows-e493f911269a2dabab7b05ec28726cdaeffb660e.tar.xz
wireguard-windows-e493f911269a2dabab7b05ec28726cdaeffb660e.zip
service: split into tunnel and manager
Diffstat (limited to 'tunnel')
-rw-r--r--tunnel/defaultroutemonitor.go150
-rw-r--r--tunnel/firewall/blocker.go201
-rw-r--r--tunnel/firewall/helpers.go105
-rw-r--r--tunnel/firewall/mksyscall.go8
-rw-r--r--tunnel/firewall/rules.go1240
-rw-r--r--tunnel/firewall/syscall_windows.go42
-rw-r--r--tunnel/firewall/types_windows.go417
-rw-r--r--tunnel/firewall/types_windows_386.go88
-rw-r--r--tunnel/firewall/types_windows_amd64.go85
-rw-r--r--tunnel/firewall/types_windows_test.go538
-rw-r--r--tunnel/firewall/zsyscall_windows.go186
-rw-r--r--tunnel/ifaceconfig.go220
-rw-r--r--tunnel/service.go225
13 files changed, 3505 insertions, 0 deletions
diff --git a/tunnel/defaultroutemonitor.go b/tunnel/defaultroutemonitor.go
new file mode 100644
index 00000000..1ffce5fa
--- /dev/null
+++ b/tunnel/defaultroutemonitor.go
@@ -0,0 +1,150 @@
+/* SPDX-License-Identifier: MIT
+ *
+ * Copyright (C) 2019 WireGuard LLC. All Rights Reserved.
+ */
+
+package tunnel
+
+import (
+ "log"
+ "time"
+
+ "golang.org/x/sys/windows"
+ "golang.zx2c4.com/winipcfg"
+ "golang.zx2c4.com/wireguard/device"
+ "golang.zx2c4.com/wireguard/tun"
+)
+
+func bindSocketRoute(family winipcfg.AddressFamily, device *device.Device, ourLUID uint64, lastLUID *uint64, lastIndex *uint32) error {
+ routes, err := winipcfg.GetRoutes(family)
+ if err != nil {
+ return err
+ }
+ lowestMetric := ^uint32(0)
+ index := uint32(0) // Zero is "unspecified", which for IP_UNICAST_IF resets the value, which is what we want.
+ luid := uint64(0) // Hopefully luid zero is unspecified, but hard to find docs saying so.
+ for _, route := range routes {
+ if route.DestinationPrefix.PrefixLength != 0 || route.InterfaceLUID == ourLUID {
+ continue
+ }
+ ifrow, err := winipcfg.GetIfRow(route.InterfaceLUID)
+ if err != nil || ifrow.OperStatus != winipcfg.IfOperStatusUp {
+ log.Printf("Found default route for interface %d, but not up, so skipping", route.InterfaceIndex)
+ continue
+ }
+ if route.Metric < lowestMetric {
+ lowestMetric = route.Metric
+ index = route.InterfaceIndex
+ luid = route.InterfaceLUID
+ }
+ }
+ if luid == *lastLUID && index == *lastIndex {
+ return nil
+ }
+ *lastLUID = luid
+ *lastIndex = index
+ if family == windows.AF_INET {
+ log.Printf("Binding UDPv4 socket to interface %d", index)
+ return device.BindSocketToInterface4(index)
+ } else if family == windows.AF_INET6 {
+ log.Printf("Binding UDPv6 socket to interface %d", index)
+ return device.BindSocketToInterface6(index)
+ }
+ return nil
+}
+
+func getIPInterfaceRetry(luid uint64, family winipcfg.AddressFamily, retry bool) (ipi *winipcfg.IPInterface, err error) {
+ const maxRetries = 100
+ for i := 0; i < maxRetries; i++ {
+ ipi, err = winipcfg.GetIPInterface(luid, family)
+ if retry && i != maxRetries-1 && err == windows.ERROR_NOT_FOUND {
+ time.Sleep(time.Millisecond * 50)
+ continue
+ }
+ break
+ }
+ return
+}
+
+func monitorDefaultRoutes(device *device.Device, autoMTU bool, tun *tun.NativeTun) (*winipcfg.RouteChangeCallback, error) {
+ ourLUID := tun.LUID()
+ lastLUID4 := uint64(0)
+ lastLUID6 := uint64(0)
+ lastIndex4 := uint32(0)
+ lastIndex6 := uint32(0)
+ lastMTU := uint32(0)
+ doIt := func(retry bool) error {
+ err := bindSocketRoute(windows.AF_INET, device, ourLUID, &lastLUID4, &lastIndex4)
+ if err != nil {
+ return err
+ }
+ err = bindSocketRoute(windows.AF_INET6, device, ourLUID, &lastLUID6, &lastIndex6)
+ if err != nil {
+ return err
+ }
+ if !autoMTU {
+ return nil
+ }
+ mtu := uint32(0)
+ if lastLUID4 != 0 {
+ iface, err := winipcfg.InterfaceFromLUID(lastLUID4)
+ if err != nil {
+ return err
+ }
+ if iface.MTU > 0 {
+ mtu = iface.MTU
+ }
+ }
+ if lastLUID6 != 0 {
+ iface, err := winipcfg.InterfaceFromLUID(lastLUID6)
+ if err != nil {
+ return err
+ }
+ if iface.MTU > 0 && iface.MTU < mtu {
+ mtu = iface.MTU
+ }
+ }
+ if mtu > 0 && (lastMTU == 0 || lastMTU != mtu) {
+ iface, err := getIPInterfaceRetry(ourLUID, windows.AF_INET, retry)
+ if err != nil {
+ return err
+ }
+ iface.NLMTU = mtu - 80
+ if iface.NLMTU < 576 {
+ iface.NLMTU = 576
+ }
+ err = iface.Set()
+ if err != nil {
+ return err
+ }
+ tun.ForceMTU(int(iface.NLMTU)) //TODO: it sort of breaks the model with v6 mtu and v4 mtu being different. Just set v4 one for now.
+ iface, err = getIPInterfaceRetry(ourLUID, windows.AF_INET6, retry)
+ if err != nil {
+ return err
+ }
+ iface.NLMTU = mtu - 80
+ if iface.NLMTU < 1280 {
+ iface.NLMTU = 1280
+ }
+ err = iface.Set()
+ if err != nil {
+ return err
+ }
+ lastMTU = mtu
+ }
+ return nil
+ }
+ err := doIt(true)
+ if err != nil {
+ return nil, err
+ }
+ cb, err := winipcfg.RegisterRouteChangeCallback(func(notificationType winipcfg.MibNotificationType, route *winipcfg.Route) {
+ if route.DestinationPrefix.PrefixLength == 0 {
+ _ = doIt(false)
+ }
+ })
+ if err != nil {
+ return nil, err
+ }
+ return cb, nil
+}
diff --git a/tunnel/firewall/blocker.go b/tunnel/firewall/blocker.go
new file mode 100644
index 00000000..8034935d
--- /dev/null
+++ b/tunnel/firewall/blocker.go
@@ -0,0 +1,201 @@
+/* SPDX-License-Identifier: MIT
+ *
+ * Copyright (C) 2019 WireGuard LLC. All Rights Reserved.
+ */
+
+package firewall
+
+import (
+ "errors"
+ "net"
+ "unsafe"
+
+ "golang.org/x/sys/windows"
+)
+
+type wfpObjectInstaller func(uintptr) error
+
+//
+// Fundamental WireGuard specific WFP objects.
+//
+type baseObjects struct {
+ provider windows.GUID
+ filters windows.GUID
+}
+
+var wfpSession uintptr
+
+func createWfpSession() (uintptr, error) {
+ sessionDisplayData, err := createWtFwpmDisplayData0("WireGuard", "WireGuard dynamic session")
+ if err != nil {
+ return 0, wrapErr(err)
+ }
+
+ session := wtFwpmSession0{
+ displayData: *sessionDisplayData,
+ flags: cFWPM_SESSION_FLAG_DYNAMIC,
+ txnWaitTimeoutInMSec: windows.INFINITE,
+ }
+
+ sessionHandle := uintptr(0)
+
+ err = fwpmEngineOpen0(nil, cRPC_C_AUTHN_WINNT, nil, &session, unsafe.Pointer(&sessionHandle))
+ if err != nil {
+ return 0, wrapErr(err)
+ }
+
+ return sessionHandle, nil
+}
+
+func registerBaseObjects(session uintptr) (*baseObjects, error) {
+ // {48E29F38-7492-4436-8F92-29D78A8D29D3}
+ providerGUID := windows.GUID{
+ Data1: 0x48e29f38,
+ Data2: 0x7492,
+ Data3: 0x4436,
+ Data4: [8]byte{0x8f, 0x92, 0x29, 0xd7, 0x8a, 0x8d, 0x29, 0xd3},
+ }
+ // {FE3DB7F8-4658-4DE5-8DA9-CE5086A8266B}
+ filtersGUID := windows.GUID{
+ Data1: 0xfe3db7f8,
+ Data2: 0x4658,
+ Data3: 0x4de5,
+ Data4: [8]byte{0x8d, 0xa9, 0xce, 0x50, 0x86, 0xa8, 0x26, 0x6b},
+ }
+
+ //
+ // Register provider.
+ //
+ {
+ displayData, err := createWtFwpmDisplayData0("WireGuard", "The WireGuard provider")
+ if err != nil {
+ return nil, wrapErr(err)
+ }
+ provider := wtFwpmProvider0{
+ providerKey: providerGUID,
+ displayData: *displayData,
+ }
+ err = fwpmProviderAdd0(session, &provider, 0)
+ if err != nil {
+ //TODO: cleanup entire call chain of these if failure?
+ return nil, wrapErr(err)
+ }
+ }
+
+ //
+ // Register filters sublayer.
+ //
+ {
+ displayData, err := createWtFwpmDisplayData0("WireGuard filters", "Permissive and blocking filters")
+ if err != nil {
+ return nil, wrapErr(err)
+ }
+ sublayer := wtFwpmSublayer0{
+ subLayerKey: filtersGUID,
+ displayData: *displayData,
+ providerKey: &providerGUID,
+ weight: ^uint16(0),
+ }
+ err = fwpmSubLayerAdd0(session, &sublayer, 0)
+ if err != nil {
+ return nil, wrapErr(err)
+ }
+ }
+
+ return &baseObjects{
+ providerGUID,
+ filtersGUID,
+ }, nil
+}
+
+func EnableFirewall(luid uint64, restrictToDNSServers []net.IP, restrictAll bool) error {
+ if wfpSession != 0 {
+ return errors.New("The firewall has already been enabled")
+ }
+
+ session, err := createWfpSession()
+ if err != nil {
+ return wrapErr(err)
+ }
+
+ objectInstaller := func(session uintptr) error {
+ baseObjects, err := registerBaseObjects(session)
+ if err != nil {
+ return wrapErr(err)
+ }
+
+ if len(restrictToDNSServers) > 0 {
+ err = blockDNS(restrictToDNSServers, session, baseObjects, 15, 14)
+ if err != nil {
+ return wrapErr(err)
+ }
+ }
+
+ if restrictAll {
+ err = permitLoopback(session, baseObjects, 13)
+ if err != nil {
+ return wrapErr(err)
+ }
+ }
+
+ err = permitTunInterface(session, baseObjects, 12, luid)
+ if err != nil {
+ return wrapErr(err)
+ }
+
+ err = permitWireGuardService(session, baseObjects, 12)
+ if err != nil {
+ return wrapErr(err)
+ }
+
+ if restrictAll {
+ err = permitDHCPIPv4(session, baseObjects, 12)
+ if err != nil {
+ return wrapErr(err)
+ }
+
+ err = permitDHCPIPv6(session, baseObjects, 12)
+ if err != nil {
+ return wrapErr(err)
+ }
+
+ err = permitNdp(session, baseObjects, 12)
+ if err != nil {
+ return wrapErr(err)
+ }
+
+ /* TODO: actually evaluate if this does anything and if we need this. It's layer 2; our other rules are layer 3.
+ * In other words, if somebody complains, try enabling it. For now, keep it off.
+ err = permitHyperV(session, baseObjects, 12)
+ if err != nil {
+ return wrapErr(err)
+ }
+ */
+ }
+
+ if restrictAll {
+ err = blockAll(session, baseObjects, 0)
+ if err != nil {
+ return wrapErr(err)
+ }
+ }
+
+ return nil
+ }
+
+ err = runTransaction(session, objectInstaller)
+ if err != nil {
+ fwpmEngineClose0(session)
+ return wrapErr(err)
+ }
+
+ wfpSession = session
+ return nil
+}
+
+func DisableFirewall() {
+ if wfpSession != 0 {
+ fwpmEngineClose0(wfpSession)
+ wfpSession = 0
+ }
+}
diff --git a/tunnel/firewall/helpers.go b/tunnel/firewall/helpers.go
new file mode 100644
index 00000000..e340b802
--- /dev/null
+++ b/tunnel/firewall/helpers.go
@@ -0,0 +1,105 @@
+/* SPDX-License-Identifier: MIT
+ *
+ * Copyright (C) 2019 WireGuard LLC. All Rights Reserved.
+ */
+
+package firewall
+
+import (
+ "fmt"
+ "os"
+ "runtime"
+ "syscall"
+ "unsafe"
+
+ "golang.org/x/sys/windows"
+)
+
+func runTransaction(session uintptr, operation wfpObjectInstaller) error {
+ err := fwpmTransactionBegin0(session, 0)
+ if err != nil {
+ return wrapErr(err)
+ }
+
+ err = operation(session)
+ if err != nil {
+ fwpmTransactionAbort0(session)
+ return wrapErr(err)
+ }
+
+ err = fwpmTransactionCommit0(session)
+ if err != nil {
+ fwpmTransactionAbort0(session)
+ return wrapErr(err)
+ }
+
+ return nil
+}
+
+func createWtFwpmDisplayData0(name, description string) (*wtFwpmDisplayData0, error) {
+ namePtr, err := windows.UTF16PtrFromString(name)
+ if err != nil {
+ return nil, wrapErr(err)
+ }
+
+ descriptionPtr, err := windows.UTF16PtrFromString(description)
+ if err != nil {
+ return nil, wrapErr(err)
+ }
+
+ return &wtFwpmDisplayData0{
+ name: namePtr,
+ description: descriptionPtr,
+ }, nil
+}
+
+func filterWeight(weight uint8) wtFwpValue0 {
+ return wtFwpValue0{
+ _type: cFWP_UINT8,
+ value: uintptr(weight),
+ }
+}
+
+func wrapErr(err error) error {
+ if _, ok := err.(syscall.Errno); !ok {
+ return err
+ }
+ _, file, line, ok := runtime.Caller(1)
+ if !ok {
+ return fmt.Errorf("Firewall error at unknown location: %v", err)
+ }
+ return fmt.Errorf("Firewall error at %s:%d: %v", file, line, err)
+}
+
+func getCurrentProcessSecurityDescriptor() (*wtFwpByteBlob, error) {
+ procHandle, err := windows.GetCurrentProcess()
+ if err != nil {
+ panic(err)
+ }
+ blob := &wtFwpByteBlob{}
+ err = getSecurityInfo(procHandle, cSE_KERNEL_OBJECT, cDACL_SECURITY_INFORMATION, nil, nil, nil, nil, (*uintptr)(unsafe.Pointer(&blob.data)))
+ if err != nil {
+ return nil, wrapErr(err)
+ }
+ blob.size = getSecurityDescriptorLength(uintptr(unsafe.Pointer(blob.data)))
+ return blob, nil
+}
+
+func getCurrentProcessAppID() (*wtFwpByteBlob, error) {
+ currentFile, err := os.Executable()
+ if err != nil {
+ return nil, wrapErr(err)
+ }
+
+ curFilePtr, err := windows.UTF16PtrFromString(currentFile)
+ if err != nil {
+ return nil, wrapErr(err)
+ }
+
+ var appID *wtFwpByteBlob
+ err = fwpmGetAppIdFromFileName0(curFilePtr, unsafe.Pointer(&appID))
+ if err != nil {
+ return nil, wrapErr(err)
+ }
+ return appID, nil
+}
diff --git a/tunnel/firewall/mksyscall.go b/tunnel/firewall/mksyscall.go
new file mode 100644
index 00000000..5e4bcaa0
--- /dev/null
+++ b/tunnel/firewall/mksyscall.go
@@ -0,0 +1,8 @@
+/* SPDX-License-Identifier: MIT
+ *
+ * Copyright (C) 2019 WireGuard LLC. All Rights Reserved.
+ */
+
+package firewall
+
+//go:generate go run $GOROOT/src/syscall/mksyscall_windows.go -output zsyscall_windows.go syscall_windows.go
diff --git a/tunnel/firewall/rules.go b/tunnel/firewall/rules.go
new file mode 100644
index 00000000..76e2a85b
--- /dev/null
+++ b/tunnel/firewall/rules.go
@@ -0,0 +1,1240 @@
+/* SPDX-License-Identifier: MIT
+ *
+ * Copyright (C) 2019 WireGuard LLC. All Rights Reserved.
+ */
+
+package firewall
+
+import (
+ "encoding/binary"
+ "errors"
+ "net"
+ "runtime"
+ "unsafe"
+
+ "golang.org/x/sys/windows"
+ "golang.zx2c4.com/wireguard/windows/version"
+)
+
+//
+// Known addresses.
+//
+var (
+ linkLocal = wtFwpV6AddrAndMask{[16]uint8{0xfe, 0x80}, 10}
+
+ linkLocalDHCPMulticast = wtFwpByteArray16{[16]uint8{0xFF, 0x02, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1, 0x0, 0x2}}
+ siteLocalDHCPMulticast = wtFwpByteArray16{[16]uint8{0xFF, 0x05, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1, 0x0, 0x3}}
+
+ linkLocalRouterMulticast = wtFwpByteArray16{[16]uint8{0xFF, 0x02, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x2}}
+)
+
+func permitTunInterface(session uintptr, baseObjects *baseObjects, weight uint8, ifLUID uint64) error {
+ ifaceCondition := wtFwpmFilterCondition0{
+ fieldKey: cFWPM_CONDITION_IP_LOCAL_INTERFACE,
+ matchType: cFWP_MATCH_EQUAL,
+ conditionValue: wtFwpConditionValue0{
+ _type: cFWP_UINT64,
+ value: (uintptr)(unsafe.Pointer(&ifLUID)),
+ },
+ }
+
+ filter := wtFwpmFilter0{
+ providerKey: &baseObjects.provider,
+ subLayerKey: baseObjects.filters,
+ weight: filterWeight(weight),
+ numFilterConditions: 1,
+ filterCondition: (*wtFwpmFilterCondition0)(unsafe.Pointer(&ifaceCondition)),
+ action: wtFwpmAction0{
+ _type: cFWP_ACTION_PERMIT,
+ },
+ }
+
+ filterID := uint64(0)
+
+ //
+ // #1 Permit outbound IPv4 traffic.
+ //
+ {
+ displayData, err := createWtFwpmDisplayData0("Permit outbound IPv4 traffic on TUN", "")
+ if err != nil {
+ return wrapErr(err)
+ }
+
+ filter.displayData = *displayData
+ filter.layerKey = cFWPM_LAYER_ALE_AUTH_CONNECT_V4
+
+ err = fwpmFilterAdd0(session, &filter, 0, &filterID)
+ if err != nil {
+ return wrapErr(err)
+ }
+ }
+
+ //
+ // #2 Permit inbound IPv4 traffic.
+ //
+ {
+ displayData, err := createWtFwpmDisplayData0("Permit inbound IPv4 traffic on TUN", "")
+ if err != nil {
+ return wrapErr(err)
+ }
+
+ filter.displayData = *displayData
+ filter.layerKey = cFWPM_LAYER_ALE_AUTH_RECV_ACCEPT_V4
+
+ err = fwpmFilterAdd0(session, &filter, 0, &filterID)
+ if err != nil {
+ return wrapErr(err)
+ }
+ }
+
+ //
+ // #3 Permit outbound IPv6 traffic.
+ //
+ {
+ displayData, err := createWtFwpmDisplayData0("Permit outbound IPv6 traffic on TUN", "")
+ if err != nil {
+ return wrapErr(err)
+ }
+
+ filter.displayData = *displayData
+ filter.layerKey = cFWPM_LAYER_ALE_AUTH_CONNECT_V6
+
+ err = fwpmFilterAdd0(session, &filter, 0, &filterID)
+ if err != nil {
+ return wrapErr(err)
+ }
+ }
+
+ //
+ // #4 Permit inbound IPv6 traffic.
+ //
+ {
+ displayData, err := createWtFwpmDisplayData0("Permit inbound IPv6 traffic on TUN", "")
+ if err != nil {
+ return wrapErr(err)
+ }
+
+ filter.displayData = *displayData
+ filter.layerKey = cFWPM_LAYER_ALE_AUTH_RECV_ACCEPT_V6
+
+ err = fwpmFilterAdd0(session, &filter, 0, &filterID)
+ if err != nil {
+ return wrapErr(err)
+ }
+ }
+
+ return nil
+}
+
+func permitWireGuardService(session uintptr, baseObjects *baseObjects, weight uint8) error {
+ var conditions [2]wtFwpmFilterCondition0
+
+ //
+ // First condition is the exe path of the current process.
+ //
+ appID, err := getCurrentProcessAppID()
+ if err != nil {
+ return wrapErr(err)
+ }
+ defer fwpmFreeMemory0(unsafe.Pointer(&appID))
+
+ conditions[0] = wtFwpmFilterCondition0{
+ fieldKey: cFWPM_CONDITION_ALE_APP_ID,
+ matchType: cFWP_MATCH_EQUAL,
+ conditionValue: wtFwpConditionValue0{
+ _type: cFWP_BYTE_BLOB_TYPE,
+ value: uintptr(unsafe.Pointer(appID)),
+ },
+ }
+
+ //
+ // Second condition is the SECURITY_DESCRIPTOR of the current process.
+ // This prevents low privileged applications hosted in the same exe from matching this filter.
+ //
+ sd, err := getCurrentProcessSecurityDescriptor()
+ if err != nil {
+ return wrapErr(err)
+ }
+ defer windows.LocalFree(windows.Handle(unsafe.Pointer(sd.data)))
+
+ conditions[1] = wtFwpmFilterCondition0{
+ fieldKey: cFWPM_CONDITION_ALE_USER_ID,
+ matchType: cFWP_MATCH_EQUAL,
+ conditionValue: wtFwpConditionValue0{
+ _type: cFWP_SECURITY_DESCRIPTOR_TYPE,
+ value: uintptr(unsafe.Pointer(sd)),
+ },
+ }
+
+ //
+ // Assemble the filter.
+ //
+ filter := wtFwpmFilter0{
+ providerKey: &baseObjects.provider,
+ subLayerKey: baseObjects.filters,
+ weight: filterWeight(weight),
+ flags: cFWPM_FILTER_FLAG_CLEAR_ACTION_RIGHT,
+ numFilterConditions: uint32(len(conditions)),
+ filterCondition: (*wtFwpmFilterCondition0)(unsafe.Pointer(&conditions)),
+ action: wtFwpmAction0{
+ _type: cFWP_ACTION_PERMIT,
+ },
+ }
+
+ filterID := uint64(0)
+
+ //
+ // #1 Permit outbound IPv4 traffic.
+ //
+ {
+ displayData, err := createWtFwpmDisplayData0("Permit unrestricted outbound traffic for WireGuard service (IPv4)", "")
+ if err != nil {
+ return wrapErr(err)
+ }
+
+ filter.displayData = *displayData
+ filter.layerKey = cFWPM_LAYER_ALE_AUTH_CONNECT_V4
+
+ err = fwpmFilterAdd0(session, &filter, 0, &filterID)
+ if err != nil {
+ return wrapErr(err)
+ }
+ }
+
+ //
+ // #2 Permit inbound IPv4 traffic.
+ //
+ {
+ displayData, err := createWtFwpmDisplayData0("Permit unrestricted inbound traffic for WireGuard service (IPv4)", "")
+ if err != nil {
+ return wrapErr(err)
+ }
+
+ filter.displayData = *displayData
+ filter.layerKey = cFWPM_LAYER_ALE_AUTH_RECV_ACCEPT_V4
+
+ err = fwpmFilterAdd0(session, &filter, 0, &filterID)
+ if err != nil {
+ return wrapErr(err)
+ }
+ }
+
+ //
+ // #3 Permit outbound IPv6 traffic.
+ //
+ {
+ displayData, err := createWtFwpmDisplayData0("Permit unrestricted outbound traffic for WireGuard service (IPv6)", "")
+ if err != nil {
+ return wrapErr(err)
+ }
+
+ filter.displayData = *displayData
+ filter.layerKey = cFWPM_LAYER_ALE_AUTH_CONNECT_V6
+
+ err = fwpmFilterAdd0(session, &filter, 0, &filterID)
+ if err != nil {
+ return wrapErr(err)
+ }
+ }
+
+ //
+ // #4 Permit inbound IPv6 traffic.
+ //
+ {
+ displayData, err := createWtFwpmDisplayData0("Permit unrestricted inbound traffic for WireGuard service (IPv6)", "")
+ if err != nil {
+ return wrapErr(err)
+ }
+
+ filter.displayData = *displayData
+ filter.layerKey = cFWPM_LAYER_ALE_AUTH_RECV_ACCEPT_V6
+
+ err = fwpmFilterAdd0(session, &filter, 0, &filterID)
+ if err != nil {
+ return wrapErr(err)
+ }
+ }
+
+ return nil
+}
+
+func permitLoopback(session uintptr, baseObjects *baseObjects, weight uint8) error {
+ condition := wtFwpmFilterCondition0{
+ fieldKey: cFWPM_CONDITION_INTERFACE_TYPE,
+ matchType: cFWP_MATCH_EQUAL,
+ conditionValue: wtFwpConditionValue0{
+ _type: cFWP_UINT32,
+ value: uintptr(cIF_TYPE_SOFTWARE_LOOPBACK),
+ },
+ }
+
+ filter := wtFwpmFilter0{
+ providerKey: &baseObjects.provider,
+ subLayerKey: baseObjects.filters,
+ weight: filterWeight(weight),
+ numFilterConditions: 1,
+ filterCondition: (*wtFwpmFilterCondition0)(unsafe.Pointer(&condition)),
+ action: wtFwpmAction0{
+ _type: cFWP_ACTION_PERMIT,
+ },
+ }
+
+ filterID := uint64(0)
+
+ //
+ // #1 Permit outbound IPv4 on loopback.
+ //
+ {
+ displayData, err := createWtFwpmDisplayData0("Permit outbound on loopback (IPv4)", "")
+ if err != nil {
+ return wrapErr(err)
+ }
+
+ filter.displayData = *displayData
+ filter.layerKey = cFWPM_LAYER_ALE_AUTH_CONNECT_V4
+
+ err = fwpmFilterAdd0(session, &filter, 0, &filterID)
+ if err != nil {
+ return wrapErr(err)
+ }
+ }
+
+ //
+ // #2 Permit inbound IPv4 on loopback.
+ //
+ {
+ displayData, err := createWtFwpmDisplayData0("Permit inbound on loopback (IPv4)", "")
+ if err != nil {
+ return wrapErr(err)
+ }
+
+ filter.displayData = *displayData
+ filter.layerKey = cFWPM_LAYER_ALE_AUTH_RECV_ACCEPT_V4
+
+ err = fwpmFilterAdd0(session, &filter, 0, &filterID)
+ if err != nil {
+ return wrapErr(err)
+ }
+ }
+
+ //
+ // #3 Permit outbound IPv6 on loopback.
+ //
+ {
+ displayData, err := createWtFwpmDisplayData0("Permit outbound on loopback (IPv6)", "")
+ if err != nil {
+ return wrapErr(err)
+ }
+
+ filter.displayData = *displayData
+ filter.layerKey = cFWPM_LAYER_ALE_AUTH_CONNECT_V6
+
+ err = fwpmFilterAdd0(session, &filter, 0, &filterID)
+ if err != nil {
+ return wrapErr(err)
+ }
+ }
+
+ //
+ // #4 Permit inbound IPv6 on loopback.
+ //
+ {
+ displayData, err := createWtFwpmDisplayData0("Permit inbound on loopback (IPv6)", "")
+ if err != nil {
+ return wrapErr(err)
+ }
+
+ filter.displayData = *displayData
+ filter.layerKey = cFWPM_LAYER_ALE_AUTH_RECV_ACCEPT_V6
+
+ err = fwpmFilterAdd0(session, &filter, 0, &filterID)
+ if err != nil {
+ return wrapErr(err)
+ }
+ }
+
+ return nil
+}
+
+func permitDHCPIPv4(session uintptr, baseObjects *baseObjects, weight uint8) error {
+ //
+ // #1 Outbound DHCP request on IPv4.
+ //
+ {
+ var conditions [4]wtFwpmFilterCondition0
+
+ conditions[0].fieldKey = cFWPM_CONDITION_IP_PROTOCOL
+ conditions[0].matchType = cFWP_MATCH_EQUAL
+ conditions[0].conditionValue._type = cFWP_UINT8
+ conditions[0].conditionValue.value = uintptr(cIPPROTO_UDP)
+
+ conditions[1].fieldKey = cFWPM_CONDITION_IP_LOCAL_PORT
+ conditions[1].matchType = cFWP_MATCH_EQUAL
+ conditions[1].conditionValue._type = cFWP_UINT16
+ conditions[1].conditionValue.value = uintptr(68)
+
+ conditions[2].fieldKey = cFWPM_CONDITION_IP_REMOTE_PORT
+ conditions[2].matchType = cFWP_MATCH_EQUAL
+ conditions[2].conditionValue._type = cFWP_UINT16
+ conditions[2].conditionValue.value = uintptr(67)
+
+ conditions[3].fieldKey = cFWPM_CONDITION_IP_REMOTE_ADDRESS
+ conditions[3].matchType = cFWP_MATCH_EQUAL
+ conditions[3].conditionValue._type = cFWP_UINT32
+ conditions[3].conditionValue.value = uintptr(0xffffffff)
+
+ displayData, err := createWtFwpmDisplayData0("Permit outbound DHCP request (IPv4)", "")
+ if err != nil {
+ return wrapErr(err)
+ }
+
+ filter := wtFwpmFilter0{
+ displayData: *displayData,
+ providerKey: &baseObjects.provider,
+ layerKey: cFWPM_LAYER_ALE_AUTH_CONNECT_V4,
+ subLayerKey: baseObjects.filters,
+ weight: filterWeight(weight),
+ numFilterConditions: uint32(len(conditions)),
+ filterCondition: (*wtFwpmFilterCondition0)(unsafe.Pointer(&conditions)),
+ action: wtFwpmAction0{
+ _type: cFWP_ACTION_PERMIT,
+ },
+ }
+
+ filterID := uint64(0)
+
+ err = fwpmFilterAdd0(session, &filter, 0, &filterID)
+ if err != nil {
+ return wrapErr(err)
+ }
+ }
+
+ //
+ // #2 Inbound DHCP response on IPv4.
+ //
+ {
+ var conditions [3]wtFwpmFilterCondition0
+
+ conditions[0].fieldKey = cFWPM_CONDITION_IP_PROTOCOL
+ conditions[0].matchType = cFWP_MATCH_EQUAL
+ conditions[0].conditionValue._type = cFWP_UINT8
+ conditions[0].conditionValue.value = uintptr(cIPPROTO_UDP)
+
+ conditions[1].fieldKey = cFWPM_CONDITION_IP_LOCAL_PORT
+ conditions[1].matchType = cFWP_MATCH_EQUAL
+ conditions[1].conditionValue._type = cFWP_UINT16
+ conditions[1].conditionValue.value = uintptr(68)
+
+ conditions[2].fieldKey = cFWPM_CONDITION_IP_REMOTE_PORT
+ conditions[2].matchType = cFWP_MATCH_EQUAL
+ conditions[2].conditionValue._type = cFWP_UINT16
+ conditions[2].conditionValue.value = uintptr(67)
+
+ displayData, err := createWtFwpmDisplayData0("Permit inbound DHCP response (IPv4)", "")
+ if err != nil {
+ return wrapErr(err)
+ }
+
+ filter := wtFwpmFilter0{
+ displayData: *displayData,
+ providerKey: &baseObjects.provider,
+ layerKey: cFWPM_LAYER_ALE_AUTH_RECV_ACCEPT_V4,
+ subLayerKey: baseObjects.filters,
+ weight: filterWeight(weight),
+ numFilterConditions: uint32(len(conditions)),
+ filterCondition: (*wtFwpmFilterCondition0)(unsafe.Pointer(&conditions)),
+ action: wtFwpmAction0{
+ _type: cFWP_ACTION_PERMIT,
+ },
+ }
+
+ filterID := uint64(0)
+
+ err = fwpmFilterAdd0(session, &filter, 0, &filterID)
+ if err != nil {
+ return wrapErr(err)
+ }
+ }
+
+ return nil
+}
+
+func permitDHCPIPv6(session uintptr, baseObjects *baseObjects, weight uint8) error {
+ //
+ // #1 Outbound DHCP request on IPv6.
+ //
+ {
+ var conditions [6]wtFwpmFilterCondition0
+
+ conditions[0].fieldKey = cFWPM_CONDITION_IP_PROTOCOL
+ conditions[0].matchType = cFWP_MATCH_EQUAL
+ conditions[0].conditionValue._type = cFWP_UINT8
+ conditions[0].conditionValue.value = uintptr(cIPPROTO_UDP)
+
+ conditions[1].fieldKey = cFWPM_CONDITION_IP_REMOTE_ADDRESS
+ conditions[1].matchType = cFWP_MATCH_EQUAL
+ conditions[1].conditionValue._type = cFWP_BYTE_ARRAY16_TYPE
+ conditions[1].conditionValue.value = uintptr(unsafe.Pointer(&linkLocalDHCPMulticast))
+
+ // Repeat the condition type for logical OR.
+ conditions[2].fieldKey = cFWPM_CONDITION_IP_REMOTE_ADDRESS
+ conditions[2].matchType = cFWP_MATCH_EQUAL
+ conditions[2].conditionValue._type = cFWP_BYTE_ARRAY16_TYPE
+ conditions[2].conditionValue.value = uintptr(unsafe.Pointer(&siteLocalDHCPMulticast))
+
+ conditions[3].fieldKey = cFWPM_CONDITION_IP_REMOTE_PORT
+ conditions[3].matchType = cFWP_MATCH_EQUAL
+ conditions[3].conditionValue._type = cFWP_UINT16
+ conditions[3].conditionValue.value = uintptr(547)
+
+ conditions[4].fieldKey = cFWPM_CONDITION_IP_LOCAL_ADDRESS
+ conditions[4].matchType = cFWP_MATCH_EQUAL
+ conditions[4].conditionValue._type = cFWP_V6_ADDR_MASK
+ conditions[4].conditionValue.value = uintptr(unsafe.Pointer(&linkLocal))
+
+ conditions[5].fieldKey = cFWPM_CONDITION_IP_LOCAL_PORT
+ conditions[5].matchType = cFWP_MATCH_EQUAL
+ conditions[5].conditionValue._type = cFWP_UINT16
+ conditions[5].conditionValue.value = uintptr(546)
+
+ displayData, err := createWtFwpmDisplayData0("Permit outbound DHCP request (IPv6)", "")
+ if err != nil {
+ return wrapErr(err)
+ }
+
+ filter := wtFwpmFilter0{
+ displayData: *displayData,
+ providerKey: &baseObjects.provider,
+ layerKey: cFWPM_LAYER_ALE_AUTH_CONNECT_V6,
+ subLayerKey: baseObjects.filters,
+ weight: filterWeight(weight),
+ numFilterConditions: uint32(len(conditions)),
+ filterCondition: (*wtFwpmFilterCondition0)(unsafe.Pointer(&conditions)),
+ action: wtFwpmAction0{
+ _type: cFWP_ACTION_PERMIT,
+ },
+ }
+
+ filterID := uint64(0)
+
+ err = fwpmFilterAdd0(session, &filter, 0, &filterID)
+ if err != nil {
+ return wrapErr(err)
+ }
+ }
+
+ //
+ // #2 Inbound DHCP response on IPv6.
+ //
+ {
+ var conditions [5]wtFwpmFilterCondition0
+
+ conditions[0].fieldKey = cFWPM_CONDITION_IP_PROTOCOL
+ conditions[0].matchType = cFWP_MATCH_EQUAL
+ conditions[0].conditionValue._type = cFWP_UINT8
+ conditions[0].conditionValue.value = uintptr(cIPPROTO_UDP)
+
+ conditions[1].fieldKey = cFWPM_CONDITION_IP_REMOTE_ADDRESS
+ conditions[1].matchType = cFWP_MATCH_EQUAL
+ conditions[1].conditionValue._type = cFWP_V6_ADDR_MASK
+ conditions[1].conditionValue.value = uintptr(unsafe.Pointer(&linkLocal))
+
+ conditions[2].fieldKey = cFWPM_CONDITION_IP_REMOTE_PORT
+ conditions[2].matchType = cFWP_MATCH_EQUAL
+ conditions[2].conditionValue._type = cFWP_UINT16
+ conditions[2].conditionValue.value = uintptr(547)
+
+ conditions[3].fieldKey = cFWPM_CONDITION_IP_LOCAL_ADDRESS
+ conditions[3].matchType = cFWP_MATCH_EQUAL
+ conditions[3].conditionValue._type = cFWP_V6_ADDR_MASK
+ conditions[3].conditionValue.value = uintptr(unsafe.Pointer(&linkLocal))
+
+ conditions[4].fieldKey = cFWPM_CONDITION_IP_LOCAL_PORT
+ conditions[4].matchType = cFWP_MATCH_EQUAL
+ conditions[4].conditionValue._type = cFWP_UINT16
+ conditions[4].conditionValue.value = uintptr(546)
+
+ displayData, err := createWtFwpmDisplayData0("Permit inbound DHCP response (IPv6)", "")
+ if err != nil {
+ return wrapErr(err)
+ }
+
+ filter := wtFwpmFilter0{
+ displayData: *displayData,
+ providerKey: &baseObjects.provider,
+ layerKey: cFWPM_LAYER_ALE_AUTH_RECV_ACCEPT_V6,
+ subLayerKey: baseObjects.filters,
+ weight: filterWeight(weight),
+ numFilterConditions: uint32(len(conditions)),
+ filterCondition: (*wtFwpmFilterCondition0)(unsafe.Pointer(&conditions)),
+ action: wtFwpmAction0{
+ _type: cFWP_ACTION_PERMIT,
+ },
+ }
+
+ filterID := uint64(0)
+
+ err = fwpmFilterAdd0(session, &filter, 0, &filterID)
+ if err != nil {
+ return wrapErr(err)
+ }
+ }
+
+ return nil
+}
+
+func permitNdp(session uintptr, baseObjects *baseObjects, weight uint8) error {
+
+ /* TODO: actually handle the hop limit somehow! The rules should vaguely be:
+ * - icmpv6 133: must be outgoing, dst must be FF02::2/128, hop limit must be 255
+ * - icmpv6 134: must be incoming, src must be FE80::/10, hop limit must be 255
+ * - icmpv6 135: either incoming or outgoing, hop limit must be 255
+ * - icmpv6 136: either incoming or outgoing, hop limit must be 255
+ * - icmpv6 137: must be incoming, src must be FE80::/10, hop limit must be 255
+ */
+
+ type filterDefinition struct {
+ displayData *wtFwpmDisplayData0
+ conditions []wtFwpmFilterCondition0
+ layer windows.GUID
+ }
+
+ var defs []filterDefinition
+
+ //
+ // Router Solicitation Message
+ // ICMP type 133, code 0. Outgoing.
+ //
+ {
+ conditions := make([]wtFwpmFilterCondition0, 4)
+
+ conditions[0].fieldKey = cFWPM_CONDITION_IP_PROTOCOL
+ conditions[0].matchType = cFWP_MATCH_EQUAL
+ conditions[0].conditionValue._type = cFWP_UINT8
+ conditions[0].conditionValue.value = uintptr(cIPPROTO_ICMPV6)
+
+ conditions[1].fieldKey = cFWPM_CONDITION_ICMP_TYPE
+ conditions[1].matchType = cFWP_MATCH_EQUAL
+ conditions[1].conditionValue._type = cFWP_UINT16
+ conditions[1].conditionValue.value = uintptr(133)
+
+ conditions[2].fieldKey = cFWPM_CONDITION_ICMP_CODE
+ conditions[2].matchType = cFWP_MATCH_EQUAL
+ conditions[2].conditionValue._type = cFWP_UINT16
+ conditions[2].conditionValue.value = uintptr(0)
+
+ conditions[3].fieldKey = cFWPM_CONDITION_IP_REMOTE_ADDRESS
+ conditions[3].matchType = cFWP_MATCH_EQUAL
+ conditions[3].conditionValue._type = cFWP_BYTE_ARRAY16_TYPE
+ conditions[3].conditionValue.value = uintptr(unsafe.Pointer(&linkLocalRouterMulticast))
+
+ displayData, err := createWtFwpmDisplayData0("Permit NDP type 133", "")
+ if err != nil {
+ return wrapErr(err)
+ }
+
+ defs = append(defs, filterDefinition{
+ displayData: displayData,
+ conditions: conditions,
+ layer: cFWPM_LAYER_ALE_AUTH_CONNECT_V6,
+ })
+ }
+
+ //
+ // Router Advertisement Message
+ // ICMP type 134, code 0. Incoming.
+ //
+ {
+ conditions := make([]wtFwpmFilterCondition0, 4)
+
+ conditions[0].fieldKey = cFWPM_CONDITION_IP_PROTOCOL
+ conditions[0].matchType = cFWP_MATCH_EQUAL
+ conditions[0].conditionValue._type = cFWP_UINT8
+ conditions[0].conditionValue.value = uintptr(cIPPROTO_ICMPV6)
+
+ conditions[1].fieldKey = cFWPM_CONDITION_ICMP_TYPE
+ conditions[1].matchType = cFWP_MATCH_EQUAL
+ conditions[1].conditionValue._type = cFWP_UINT16
+ conditions[1].conditionValue.value = uintptr(134)
+
+ conditions[2].fieldKey = cFWPM_CONDITION_ICMP_CODE
+ conditions[2].matchType = cFWP_MATCH_EQUAL
+ conditions[2].conditionValue._type = cFWP_UINT16
+ conditions[2].conditionValue.value = uintptr(0)
+
+ conditions[3].fieldKey = cFWPM_CONDITION_IP_REMOTE_ADDRESS
+ conditions[3].matchType = cFWP_MATCH_EQUAL
+ conditions[3].conditionValue._type = cFWP_V6_ADDR_MASK
+ conditions[3].conditionValue.value = uintptr(unsafe.Pointer(&linkLocal))
+
+ displayData, err := createWtFwpmDisplayData0("Permit NDP type 134", "")
+ if err != nil {
+ return wrapErr(err)
+ }
+
+ defs = append(defs, filterDefinition{
+ displayData: displayData,
+ conditions: conditions,
+ layer: cFWPM_LAYER_ALE_AUTH_RECV_ACCEPT_V6,
+ })
+ }
+
+ //
+ // Neighbor Solicitation Message
+ // ICMP type 135, code 0. Bi-directional.
+ //
+ {
+ conditions := make([]wtFwpmFilterCondition0, 3)
+
+ conditions[0].fieldKey = cFWPM_CONDITION_IP_PROTOCOL
+ conditions[0].matchType = cFWP_MATCH_EQUAL
+ conditions[0].conditionValue._type = cFWP_UINT8
+ conditions[0].conditionValue.value = uintptr(cIPPROTO_ICMPV6)
+
+ conditions[1].fieldKey = cFWPM_CONDITION_ICMP_TYPE
+ conditions[1].matchType = cFWP_MATCH_EQUAL
+ conditions[1].conditionValue._type = cFWP_UINT16
+ conditions[1].conditionValue.value = uintptr(135)
+
+ conditions[2].fieldKey = cFWPM_CONDITION_ICMP_CODE
+ conditions[2].matchType = cFWP_MATCH_EQUAL
+ conditions[2].conditionValue._type = cFWP_UINT16
+ conditions[2].conditionValue.value = uintptr(0)
+
+ displayData, err := createWtFwpmDisplayData0("Permit NDP type 135", "")
+ if err != nil {
+ return wrapErr(err)
+ }
+
+ defs = append(defs, filterDefinition{
+ displayData: displayData,
+ conditions: conditions,
+ layer: cFWPM_LAYER_ALE_AUTH_CONNECT_V6,
+ })
+
+ defs = append(defs, filterDefinition{
+ displayData: displayData,
+ conditions: conditions,
+ layer: cFWPM_LAYER_ALE_AUTH_RECV_ACCEPT_V6,
+ })
+ }
+
+ //
+ // Neighbor Advertisement Message
+ // ICMP type 136, code 0. Bi-directional.
+ //
+ {
+ conditions := make([]wtFwpmFilterCondition0, 3)
+
+ conditions[0].fieldKey = cFWPM_CONDITION_IP_PROTOCOL
+ conditions[0].matchType = cFWP_MATCH_EQUAL
+ conditions[0].conditionValue._type = cFWP_UINT8
+ conditions[0].conditionValue.value = uintptr(cIPPROTO_ICMPV6)
+
+ conditions[1].fieldKey = cFWPM_CONDITION_ICMP_TYPE
+ conditions[1].matchType = cFWP_MATCH_EQUAL
+ conditions[1].conditionValue._type = cFWP_UINT16
+ conditions[1].conditionValue.value = uintptr(136)
+
+ conditions[2].fieldKey = cFWPM_CONDITION_ICMP_CODE
+ conditions[2].matchType = cFWP_MATCH_EQUAL
+ conditions[2].conditionValue._type = cFWP_UINT16
+ conditions[2].conditionValue.value = uintptr(0)
+
+ displayData, err := createWtFwpmDisplayData0("Permit NDP type 136", "")
+ if err != nil {
+ return wrapErr(err)
+ }
+
+ defs = append(defs, filterDefinition{
+ displayData: displayData,
+ conditions: conditions,
+ layer: cFWPM_LAYER_ALE_AUTH_CONNECT_V6,
+ })
+
+ defs = append(defs, filterDefinition{
+ displayData: displayData,
+ conditions: conditions,
+ layer: cFWPM_LAYER_ALE_AUTH_RECV_ACCEPT_V6,
+ })
+ }
+
+ //
+ // Redirect Message
+ // ICMP type 137, code 0. Incoming.
+ //
+ {
+ conditions := make([]wtFwpmFilterCondition0, 4)
+
+ conditions[0].fieldKey = cFWPM_CONDITION_IP_PROTOCOL
+ conditions[0].matchType = cFWP_MATCH_EQUAL
+ conditions[0].conditionValue._type = cFWP_UINT8
+ conditions[0].conditionValue.value = uintptr(cIPPROTO_ICMPV6)
+
+ conditions[1].fieldKey = cFWPM_CONDITION_ICMP_TYPE
+ conditions[1].matchType = cFWP_MATCH_EQUAL
+ conditions[1].conditionValue._type = cFWP_UINT16
+ conditions[1].conditionValue.value = uintptr(137)
+
+ conditions[2].fieldKey = cFWPM_CONDITION_ICMP_CODE
+ conditions[2].matchType = cFWP_MATCH_EQUAL
+ conditions[2].conditionValue._type = cFWP_UINT16
+ conditions[2].conditionValue.value = uintptr(0)
+
+ conditions[3].fieldKey = cFWPM_CONDITION_IP_REMOTE_ADDRESS
+ conditions[3].matchType = cFWP_MATCH_EQUAL
+ conditions[3].conditionValue._type = cFWP_V6_ADDR_MASK
+ conditions[3].conditionValue.value = uintptr(unsafe.Pointer(&linkLocal))
+
+ displayData, err := createWtFwpmDisplayData0("Permit NDP type 137", "")
+ if err != nil {
+ return wrapErr(err)
+ }
+
+ defs = append(defs, filterDefinition{
+ displayData: displayData,
+ conditions: conditions,
+ layer: cFWPM_LAYER_ALE_AUTH_RECV_ACCEPT_V6,
+ })
+ }
+
+ filter := wtFwpmFilter0{
+ providerKey: &baseObjects.provider,
+ subLayerKey: baseObjects.filters,
+ weight: filterWeight(weight),
+ action: wtFwpmAction0{
+ _type: cFWP_ACTION_PERMIT,
+ },
+ }
+
+ filterID := uint64(0)
+
+ for _, definition := range defs {
+ filter.displayData = *definition.displayData
+ filter.layerKey = definition.layer
+ filter.numFilterConditions = uint32(len(definition.conditions))
+ filter.filterCondition = (*wtFwpmFilterCondition0)(unsafe.Pointer(&definition.conditions[0]))
+
+ err := fwpmFilterAdd0(session, &filter, 0, &filterID)
+ if err != nil {
+ return wrapErr(err)
+ }
+ }
+
+ return nil
+}
+
+func permitHyperV(session uintptr, baseObjects *baseObjects, weight uint8) error {
+ //
+ // Only applicable on Win8+.
+ //
+ {
+ v, err := version.OsVersion()
+ if err != nil {
+ panic(err)
+ }
+
+ win8plus := v.MajorVersion > 6 || (v.MajorVersion == 6 && v.MinorVersion >= 3)
+
+ if !win8plus {
+ return nil
+ }
+ }
+
+ condition := wtFwpmFilterCondition0{
+ fieldKey: cFWPM_CONDITION_L2_FLAGS,
+ matchType: cFWP_MATCH_EQUAL,
+ conditionValue: wtFwpConditionValue0{
+ _type: cFWP_UINT32,
+ value: uintptr(cFWP_CONDITION_L2_IS_VM2VM),
+ },
+ }
+
+ filter := wtFwpmFilter0{
+ providerKey: &baseObjects.provider,
+ subLayerKey: baseObjects.filters,
+ weight: filterWeight(weight),
+ numFilterConditions: 1,
+ filterCondition: (*wtFwpmFilterCondition0)(unsafe.Pointer(&condition)),
+ action: wtFwpmAction0{
+ _type: cFWP_ACTION_PERMIT,
+ },
+ }
+
+ filterID := uint64(0)
+
+ //
+ // #1 Outbound.
+ //
+ {
+ displayData, err := createWtFwpmDisplayData0("Permit Hyper-V => Hyper-V outbound", "")
+ if err != nil {
+ return wrapErr(err)
+ }
+
+ filter.displayData = *displayData
+ filter.layerKey = cFWPM_LAYER_OUTBOUND_MAC_FRAME_NATIVE
+
+ err = fwpmFilterAdd0(session, &filter, 0, &filterID)
+ if err != nil {
+ return wrapErr(err)
+ }
+ }
+
+ //
+ // #2 Inbound.
+ //
+ {
+ displayData, err := createWtFwpmDisplayData0("Permit Hyper-V => Hyper-V inbound", "")
+ if err != nil {
+ return wrapErr(err)
+ }
+
+ filter.displayData = *displayData
+ filter.layerKey = cFWPM_LAYER_INBOUND_MAC_FRAME_NATIVE
+
+ err = fwpmFilterAdd0(session, &filter, 0, &filterID)
+ if err != nil {
+ return wrapErr(err)
+ }
+ }
+
+ return nil
+}
+
+// Block all traffic except what is explicitly permitted by other rules.
+func blockAll(session uintptr, baseObjects *baseObjects, weight uint8) error {
+ filter := wtFwpmFilter0{
+ providerKey: &baseObjects.provider,
+ subLayerKey: baseObjects.filters,
+ weight: filterWeight(weight),
+ action: wtFwpmAction0{
+ _type: cFWP_ACTION_BLOCK,
+ },
+ }
+
+ filterID := uint64(0)
+
+ //
+ // #1 Block outbound traffic on IPv4.
+ //
+ {
+ displayData, err := createWtFwpmDisplayData0("Block all outbound (IPv4)", "")
+ if err != nil {
+ return wrapErr(err)
+ }
+
+ filter.displayData = *displayData
+ filter.layerKey = cFWPM_LAYER_ALE_AUTH_CONNECT_V4
+
+ err = fwpmFilterAdd0(session, &filter, 0, &filterID)
+ if err != nil {
+ return wrapErr(err)
+ }
+ }
+
+ //
+ // #2 Block inbound traffic on IPv4.
+ //
+ {
+ displayData, err := createWtFwpmDisplayData0("Block all inbound (IPv4)", "")
+ if err != nil {
+ return wrapErr(err)
+ }
+
+ filter.displayData = *displayData
+ filter.layerKey = cFWPM_LAYER_ALE_AUTH_RECV_ACCEPT_V4
+
+ err = fwpmFilterAdd0(session, &filter, 0, &filterID)
+ if err != nil {
+ return wrapErr(err)
+ }
+ }
+
+ //
+ // #3 Block outbound traffic on IPv6.
+ //
+ {
+ displayData, err := createWtFwpmDisplayData0("Block all outbound (IPv6)", "")
+ if err != nil {
+ return wrapErr(err)
+ }
+
+ filter.displayData = *displayData
+ filter.layerKey = cFWPM_LAYER_ALE_AUTH_CONNECT_V6
+
+ err = fwpmFilterAdd0(session, &filter, 0, &filterID)
+ if err != nil {
+ return wrapErr(err)
+ }
+ }
+
+ //
+ // #4 Block inbound traffic on IPv6.
+ //
+ {
+ displayData, err := createWtFwpmDisplayData0("Block all inbound (IPv6)", "")
+ if err != nil {
+ return wrapErr(err)
+ }
+
+ filter.displayData = *displayData
+ filter.layerKey = cFWPM_LAYER_ALE_AUTH_RECV_ACCEPT_V6
+
+ err = fwpmFilterAdd0(session, &filter, 0, &filterID)
+ if err != nil {
+ return wrapErr(err)
+ }
+ }
+
+ return nil
+}
+
+// Block all DNS traffic except towards specified DNS servers.
+func blockDNS(except []net.IP, session uintptr, baseObjects *baseObjects, weightAllow uint8, weightDeny uint8) error {
+ if weightDeny >= weightAllow {
+ return errors.New("The allow weight must be greater than the deny weight")
+ }
+
+ denyConditions := []wtFwpmFilterCondition0{
+ {
+ fieldKey: cFWPM_CONDITION_IP_REMOTE_PORT,
+ matchType: cFWP_MATCH_EQUAL,
+ conditionValue: wtFwpConditionValue0{
+ _type: cFWP_UINT16,
+ value: uintptr(53),
+ },
+ },
+ {
+ fieldKey: cFWPM_CONDITION_IP_PROTOCOL,
+ matchType: cFWP_MATCH_EQUAL,
+ conditionValue: wtFwpConditionValue0{
+ _type: cFWP_UINT8,
+ value: uintptr(cIPPROTO_UDP),
+ },
+ },
+ // Repeat the condition type for logical OR.
+ {
+ fieldKey: cFWPM_CONDITION_IP_PROTOCOL,
+ matchType: cFWP_MATCH_EQUAL,
+ conditionValue: wtFwpConditionValue0{
+ _type: cFWP_UINT8,
+ value: uintptr(cIPPROTO_TCP),
+ },
+ },
+ }
+
+ filter := wtFwpmFilter0{
+ providerKey: &baseObjects.provider,
+ subLayerKey: baseObjects.filters,
+ weight: filterWeight(weightDeny),
+ numFilterConditions: uint32(len(denyConditions)),
+ filterCondition: (*wtFwpmFilterCondition0)(unsafe.Pointer(&denyConditions[0])),
+ action: wtFwpmAction0{
+ _type: cFWP_ACTION_BLOCK,
+ },
+ }
+
+ filterID := uint64(0)
+
+ //
+ // #1 Block IPv4 outbound DNS.
+ //
+ {
+ displayData, err := createWtFwpmDisplayData0("Block DNS outbound (IPv4)", "")
+ if err != nil {
+ return wrapErr(err)
+ }
+
+ filter.displayData = *displayData
+ filter.layerKey = cFWPM_LAYER_ALE_AUTH_CONNECT_V4
+
+ err = fwpmFilterAdd0(session, &filter, 0, &filterID)
+ if err != nil {
+ return wrapErr(err)
+ }
+ }
+
+ //
+ // #2 Block IPv4 inbound DNS.
+ //
+ {
+ displayData, err := createWtFwpmDisplayData0("Block DNS inbound (IPv4)", "")
+ if err != nil {
+ return wrapErr(err)
+ }
+
+ filter.displayData = *displayData
+ filter.layerKey = cFWPM_LAYER_ALE_AUTH_RECV_ACCEPT_V4
+
+ err = fwpmFilterAdd0(session, &filter, 0, &filterID)
+ if err != nil {
+ return wrapErr(err)
+ }
+ }
+
+ //
+ // #3 Block IPv6 outbound DNS.
+ //
+ {
+ displayData, err := createWtFwpmDisplayData0("Block DNS outbound (IPv6)", "")
+ if err != nil {
+ return wrapErr(err)
+ }
+
+ filter.displayData = *displayData
+ filter.layerKey = cFWPM_LAYER_ALE_AUTH_CONNECT_V6
+
+ err = fwpmFilterAdd0(session, &filter, 0, &filterID)
+ if err != nil {
+ return wrapErr(err)
+ }
+ }
+
+ //
+ // #4 Block IPv6 inbound DNS.
+ //
+ {
+ displayData, err := createWtFwpmDisplayData0("Block DNS inbound (IPv6)", "")
+ if err != nil {
+ return wrapErr(err)
+ }
+
+ filter.displayData = *displayData
+ filter.layerKey = cFWPM_LAYER_ALE_AUTH_RECV_ACCEPT_V6
+
+ err = fwpmFilterAdd0(session, &filter, 0, &filterID)
+ if err != nil {
+ return wrapErr(err)
+ }
+ }
+
+ allowConditionsV4 := make([]wtFwpmFilterCondition0, 0, len(denyConditions)+len(except))
+ allowConditionsV4 = append(allowConditionsV4, denyConditions...)
+ for _, ip := range except {
+ ip4 := ip.To4()
+ if ip4 == nil {
+ continue
+ }
+ allowConditionsV4 = append(allowConditionsV4, wtFwpmFilterCondition0{
+ fieldKey: cFWPM_CONDITION_IP_REMOTE_ADDRESS,
+ matchType: cFWP_MATCH_EQUAL,
+ conditionValue: wtFwpConditionValue0{
+ _type: cFWP_UINT32,
+ value: uintptr(binary.BigEndian.Uint32(ip4)),
+ },
+ })
+ }
+
+ storedPointers := make([]*wtFwpByteArray16, 0, len(except))
+ allowConditionsV6 := make([]wtFwpmFilterCondition0, 0, len(denyConditions)+len(except))
+ allowConditionsV6 = append(allowConditionsV6, denyConditions...)
+ for _, ip := range except {
+ if ip.To4() != nil {
+ continue
+ }
+ var address wtFwpByteArray16
+ copy(address.byteArray16[:], ip)
+ allowConditionsV6 = append(allowConditionsV6, wtFwpmFilterCondition0{
+ fieldKey: cFWPM_CONDITION_IP_REMOTE_ADDRESS,
+ matchType: cFWP_MATCH_EQUAL,
+ conditionValue: wtFwpConditionValue0{
+ _type: cFWP_BYTE_ARRAY16_TYPE,
+ value: uintptr(unsafe.Pointer(&address)),
+ },
+ })
+ storedPointers = append(storedPointers, &address)
+ }
+
+ filter = wtFwpmFilter0{
+ providerKey: &baseObjects.provider,
+ subLayerKey: baseObjects.filters,
+ weight: filterWeight(weightAllow),
+ numFilterConditions: uint32(len(allowConditionsV4)),
+ filterCondition: (*wtFwpmFilterCondition0)(unsafe.Pointer(&allowConditionsV4[0])),
+ action: wtFwpmAction0{
+ _type: cFWP_ACTION_PERMIT,
+ },
+ }
+
+ filterID = uint64(0)
+
+ //
+ // #5 Allow IPv4 outbound DNS.
+ //
+ if len(allowConditionsV4) > len(denyConditions) {
+ displayData, err := createWtFwpmDisplayData0("Allow DNS outbound (IPv4)", "")
+ if err != nil {
+ return wrapErr(err)
+ }
+
+ filter.displayData = *displayData
+ filter.layerKey = cFWPM_LAYER_ALE_AUTH_CONNECT_V4
+
+ err = fwpmFilterAdd0(session, &filter, 0, &filterID)
+ if err != nil {
+ return wrapErr(err)
+ }
+ }
+
+ //
+ // #6 Allow IPv4 inbound DNS.
+ //
+ if len(allowConditionsV4) > len(denyConditions) {
+ displayData, err := createWtFwpmDisplayData0("Allow DNS inbound (IPv4)", "")
+ if err != nil {
+ return wrapErr(err)
+ }
+
+ filter.displayData = *displayData
+ filter.layerKey = cFWPM_LAYER_ALE_AUTH_RECV_ACCEPT_V4
+
+ err = fwpmFilterAdd0(session, &filter, 0, &filterID)
+ if err != nil {
+ return wrapErr(err)
+ }
+ }
+
+ filter.filterCondition = (*wtFwpmFilterCondition0)(unsafe.Pointer(&allowConditionsV6[0]))
+ filter.numFilterConditions = uint32(len(allowConditionsV6))
+
+ //
+ // #7 Allow IPv6 outbound DNS.
+ //
+ if len(allowConditionsV6) > len(denyConditions) {
+ displayData, err := createWtFwpmDisplayData0("Allow DNS outbound (IPv6)", "")
+ if err != nil {
+ return wrapErr(err)
+ }
+
+ filter.displayData = *displayData
+ filter.layerKey = cFWPM_LAYER_ALE_AUTH_CONNECT_V6
+
+ err = fwpmFilterAdd0(session, &filter, 0, &filterID)
+ if err != nil {
+ return wrapErr(err)
+ }
+ }
+
+ //
+ // #8 Allow IPv6 inbound DNS.
+ //
+ if len(allowConditionsV6) > len(denyConditions) {
+ displayData, err := createWtFwpmDisplayData0("Allow DNS inbound (IPv6)", "")
+ if err != nil {
+ return wrapErr(err)
+ }
+
+ filter.displayData = *displayData
+ filter.layerKey = cFWPM_LAYER_ALE_AUTH_RECV_ACCEPT_V6
+
+ err = fwpmFilterAdd0(session, &filter, 0, &filterID)
+ if err != nil {
+ return wrapErr(err)
+ }
+ }
+
+ runtime.KeepAlive(storedPointers)
+
+ return nil
+}
diff --git a/tunnel/firewall/syscall_windows.go b/tunnel/firewall/syscall_windows.go
new file mode 100644
index 00000000..5ec41b0b
--- /dev/null
+++ b/tunnel/firewall/syscall_windows.go
@@ -0,0 +1,42 @@
+/* SPDX-License-Identifier: MIT
+ *
+ * Copyright (C) 2019 WireGuard LLC. All Rights Reserved.
+ */
+
+package firewall
+
+// https://docs.microsoft.com/en-us/windows/desktop/api/fwpmu/nf-fwpmu-fwpmengineopen0
+//sys fwpmEngineOpen0(serverName *uint16, authnService wtRpcCAuthN, authIdentity *uintptr, session *wtFwpmSession0, engineHandle unsafe.Pointer) (err error) [failretval!=0] = fwpuclnt.FwpmEngineOpen0
+
+// https://docs.microsoft.com/en-us/windows/desktop/api/fwpmu/nf-fwpmu-fwpmengineclose0
+//sys fwpmEngineClose0(engineHandle uintptr) (err error) [failretval!=0] = fwpuclnt.FwpmEngineClose0
+
+// https://docs.microsoft.com/en-us/windows/desktop/api/fwpmu/nf-fwpmu-fwpmsublayeradd0
+//sys fwpmSubLayerAdd0(engineHandle uintptr, subLayer *wtFwpmSublayer0, sd uintptr) (err error) [failretval!=0] = fwpuclnt.FwpmSubLayerAdd0
+
+// https://docs.microsoft.com/en-us/windows/desktop/api/fwpmu/nf-fwpmu-fwpmgetappidfromfilename0
+//sys fwpmGetAppIdFromFileName0(fileName *uint16, appID unsafe.Pointer) (err error) [failretval!=0] = fwpuclnt.FwpmGetAppIdFromFileName0
+
+// https://docs.microsoft.com/en-us/windows/desktop/api/fwpmu/nf-fwpmu-fwpmfreememory0
+//sys fwpmFreeMemory0(p unsafe.Pointer) = fwpuclnt.FwpmFreeMemory0
+
+// https://docs.microsoft.com/en-us/windows/desktop/api/fwpmu/nf-fwpmu-fwpmfilteradd0
+//sys fwpmFilterAdd0(engineHandle uintptr, filter *wtFwpmFilter0, sd uintptr, id *uint64) (err error) [failretval!=0] = fwpuclnt.FwpmFilterAdd0
+
+// https://docs.microsoft.com/en-us/windows/desktop/api/Fwpmu/nf-fwpmu-fwpmtransactionbegin0
+//sys fwpmTransactionBegin0(engineHandle uintptr, flags uint32) (err error) [failretval!=0] = fwpuclnt.FwpmTransactionBegin0
+
+// https://docs.microsoft.com/en-us/windows/desktop/api/fwpmu/nf-fwpmu-fwpmtransactioncommit0
+//sys fwpmTransactionCommit0(engineHandle uintptr) (err error) [failretval!=0] = fwpuclnt.FwpmTransactionCommit0
+
+// https://docs.microsoft.com/en-us/windows/desktop/api/fwpmu/nf-fwpmu-fwpmtransactionabort0
+//sys fwpmTransactionAbort0(engineHandle uintptr) (err error) [failretval!=0] = fwpuclnt.FwpmTransactionAbort0
+
+// https://docs.microsoft.com/en-us/windows/desktop/api/fwpmu/nf-fwpmu-fwpmprovideradd0
+//sys fwpmProviderAdd0(engineHandle uintptr, provider *wtFwpmProvider0, sd uintptr) (err error) [failretval!=0] = fwpuclnt.FwpmProviderAdd0
+
+// https://docs.microsoft.com/sv-se/windows/desktop/api/aclapi/nf-aclapi-getsecurityinfo
+//sys getSecurityInfo(handle windows.Handle, objectType wtObjectType, si uint32, sidOwner *windows.SID, sidGroup *windows.SID, dacl *uintptr, sacl *uintptr, securityDescriptor *uintptr) (err error) [failretval!=0] = advapi32.GetSecurityInfo
+
+// https://docs.microsoft.com/en-us/windows/desktop/api/securitybaseapi/nf-securitybaseapi-getsecuritydescriptorlength
+//sys getSecurityDescriptorLength(securityDescriptor uintptr) (len uint32) = advapi32.GetSecurityDescriptorLength
diff --git a/tunnel/firewall/types_windows.go b/tunnel/firewall/types_windows.go
new file mode 100644
index 00000000..e06f7d2b
--- /dev/null
+++ b/tunnel/firewall/types_windows.go
@@ -0,0 +1,417 @@
+/* SPDX-License-Identifier: MIT
+ *
+ * Copyright (C) 2019 WireGuard LLC. All Rights Reserved.
+ */
+
+package firewall
+
+import "golang.org/x/sys/windows"
+
+const (
+ anysizeArray = 1 // ANYSIZE_ARRAY defined in winnt.h
+
+ wtFwpBitmapArray64_Size = 8
+
+ wtFwpByteArray16_Size = 16
+
+ wtFwpByteArray6_Size = 6
+
+ wtFwpmAction0_Size = 20
+ wtFwpmAction0_filterType_Offset = 4
+
+ wtFwpV4AddrAndMask_Size = 8
+ wtFwpV4AddrAndMask_mask_Offset = 4
+
+ wtFwpV6AddrAndMask_Size = 17
+ wtFwpV6AddrAndMask_prefixLength_Offset = 16
+)
+
+type wtFwpActionFlag uint32
+
+const (
+ cFWP_ACTION_FLAG_TERMINATING wtFwpActionFlag = 0x00001000
+ cFWP_ACTION_FLAG_NON_TERMINATING wtFwpActionFlag = 0x00002000
+ cFWP_ACTION_FLAG_CALLOUT wtFwpActionFlag = 0x00004000
+)
+
+// FWP_ACTION_TYPE defined in fwptypes.h
+type wtFwpActionType uint32
+
+const (
+ cFWP_ACTION_BLOCK wtFwpActionType = wtFwpActionType(0x00000001 | cFWP_ACTION_FLAG_TERMINATING)
+ cFWP_ACTION_PERMIT wtFwpActionType = wtFwpActionType(0x00000002 | cFWP_ACTION_FLAG_TERMINATING)
+ cFWP_ACTION_CALLOUT_TERMINATING wtFwpActionType = wtFwpActionType(0x00000003 | cFWP_ACTION_FLAG_CALLOUT | cFWP_ACTION_FLAG_TERMINATING)
+ cFWP_ACTION_CALLOUT_INSPECTION wtFwpActionType = wtFwpActionType(0x00000004 | cFWP_ACTION_FLAG_CALLOUT | cFWP_ACTION_FLAG_NON_TERMINATING)
+ cFWP_ACTION_CALLOUT_UNKNOWN wtFwpActionType = wtFwpActionType(0x00000005 | cFWP_ACTION_FLAG_CALLOUT)
+ cFWP_ACTION_CONTINUE wtFwpActionType = wtFwpActionType(0x00000006 | cFWP_ACTION_FLAG_NON_TERMINATING)
+ cFWP_ACTION_NONE wtFwpActionType = 0x00000007
+ cFWP_ACTION_NONE_NO_MATCH wtFwpActionType = 0x00000008
+ cFWP_ACTION_BITMAP_INDEX_SET wtFwpActionType = 0x00000009
+)
+
+// FWP_BYTE_BLOB defined in fwptypes.h
+// (https://docs.microsoft.com/en-us/windows/desktop/api/fwptypes/ns-fwptypes-fwp_byte_blob_)
+type wtFwpByteBlob struct {
+ size uint32
+ data *uint8
+}
+
+// FWP_MATCH_TYPE defined in fwptypes.h
+// (https://docs.microsoft.com/en-us/windows/desktop/api/fwptypes/ne-fwptypes-fwp_match_type_)
+type wtFwpMatchType uint32
+
+const (
+ cFWP_MATCH_EQUAL wtFwpMatchType = 0
+ cFWP_MATCH_GREATER wtFwpMatchType = cFWP_MATCH_EQUAL + 1
+ cFWP_MATCH_LESS wtFwpMatchType = cFWP_MATCH_GREATER + 1
+ cFWP_MATCH_GREATER_OR_EQUAL wtFwpMatchType = cFWP_MATCH_LESS + 1
+ cFWP_MATCH_LESS_OR_EQUAL wtFwpMatchType = cFWP_MATCH_GREATER_OR_EQUAL + 1
+ cFWP_MATCH_RANGE wtFwpMatchType = cFWP_MATCH_LESS_OR_EQUAL + 1
+ cFWP_MATCH_FLAGS_ALL_SET wtFwpMatchType = cFWP_MATCH_RANGE + 1
+ cFWP_MATCH_FLAGS_ANY_SET wtFwpMatchType = cFWP_MATCH_FLAGS_ALL_SET + 1
+ cFWP_MATCH_FLAGS_NONE_SET wtFwpMatchType = cFWP_MATCH_FLAGS_ANY_SET + 1
+ cFWP_MATCH_EQUAL_CASE_INSENSITIVE wtFwpMatchType = cFWP_MATCH_FLAGS_NONE_SET + 1
+ cFWP_MATCH_NOT_EQUAL wtFwpMatchType = cFWP_MATCH_EQUAL_CASE_INSENSITIVE + 1
+ cFWP_MATCH_PREFIX wtFwpMatchType = cFWP_MATCH_NOT_EQUAL + 1
+ cFWP_MATCH_NOT_PREFIX wtFwpMatchType = cFWP_MATCH_PREFIX + 1
+ cFWP_MATCH_TYPE_MAX wtFwpMatchType = cFWP_MATCH_NOT_PREFIX + 1
+)
+
+// FWPM_ACTION0 defined in fwpmtypes.h
+// (https://docs.microsoft.com/en-us/windows/desktop/api/fwpmtypes/ns-fwpmtypes-fwpm_action0_)
+type wtFwpmAction0 struct {
+ _type wtFwpActionType
+ filterType windows.GUID // Windows type: GUID
+}
+
+// Defined in fwpmu.h. 4cd62a49-59c3-4969-b7f3-bda5d32890a4
+var cFWPM_CONDITION_IP_LOCAL_INTERFACE = windows.GUID{
+ Data1: 0x4cd62a49,
+ Data2: 0x59c3,
+ Data3: 0x4969,
+ Data4: [8]byte{0xb7, 0xf3, 0xbd, 0xa5, 0xd3, 0x28, 0x90, 0xa4},
+}
+
+// Defined in fwpmu.h. b235ae9a-1d64-49b8-a44c-5ff3d9095045
+var cFWPM_CONDITION_IP_REMOTE_ADDRESS = windows.GUID{
+ Data1: 0xb235ae9a,
+ Data2: 0x1d64,
+ Data3: 0x49b8,
+ Data4: [8]byte{0xa4, 0x4c, 0x5f, 0xf3, 0xd9, 0x09, 0x50, 0x45},
+}
+
+// Defined in fwpmu.h. daf8cd14-e09e-4c93-a5ae-c5c13b73ffca
+var cFWPM_CONDITION_INTERFACE_TYPE = windows.GUID{
+ Data1: 0xdaf8cd14,
+ Data2: 0xe09e,
+ Data3: 0x4c93,
+ Data4: [8]byte{0xa5, 0xae, 0xc5, 0xc1, 0x3b, 0x73, 0xff, 0xca},
+}
+
+// Defined in fwpmu.h. 3971ef2b-623e-4f9a-8cb1-6e79b806b9a7
+var cFWPM_CONDITION_IP_PROTOCOL = windows.GUID{
+ Data1: 0x3971ef2b,
+ Data2: 0x623e,
+ Data3: 0x4f9a,
+ Data4: [8]byte{0x8c, 0xb1, 0x6e, 0x79, 0xb8, 0x06, 0xb9, 0xa7},
+}
+
+// Defined in fwpmu.h. 0c1ba1af-5765-453f-af22-a8f791ac775b
+var cFWPM_CONDITION_IP_LOCAL_PORT = windows.GUID{
+ Data1: 0x0c1ba1af,
+ Data2: 0x5765,
+ Data3: 0x453f,
+ Data4: [8]byte{0xaf, 0x22, 0xa8, 0xf7, 0x91, 0xac, 0x77, 0x5b},
+}
+
+// Defined in fwpmu.h. c35a604d-d22b-4e1a-91b4-68f674ee674b
+var cFWPM_CONDITION_IP_REMOTE_PORT = windows.GUID{
+ Data1: 0xc35a604d,
+ Data2: 0xd22b,
+ Data3: 0x4e1a,
+ Data4: [8]byte{0x91, 0xb4, 0x68, 0xf6, 0x74, 0xee, 0x67, 0x4b},
+}
+
+// Defined in fwpmu.h. d78e1e87-8644-4ea5-9437-d809ecefc971
+var cFWPM_CONDITION_ALE_APP_ID = windows.GUID{
+ Data1: 0xd78e1e87,
+ Data2: 0x8644,
+ Data3: 0x4ea5,
+ Data4: [8]byte{0x94, 0x37, 0xd8, 0x09, 0xec, 0xef, 0xc9, 0x71},
+}
+
+// af043a0a-b34d-4f86-979c-c90371af6e66
+var cFWPM_CONDITION_ALE_USER_ID = windows.GUID{
+ Data1: 0xaf043a0a,
+ Data2: 0xb34d,
+ Data3: 0x4f86,
+ Data4: [8]byte{0x97, 0x9c, 0xc9, 0x03, 0x71, 0xaf, 0x6e, 0x66},
+}
+
+// d9ee00de-c1ef-4617-bfe3-ffd8f5a08957
+var cFWPM_CONDITION_IP_LOCAL_ADDRESS = windows.GUID{
+ Data1: 0xd9ee00de,
+ Data2: 0xc1ef,
+ Data3: 0x4617,
+ Data4: [8]byte{0xbf, 0xe3, 0xff, 0xd8, 0xf5, 0xa0, 0x89, 0x57},
+}
+
+var cFWPM_CONDITION_ICMP_TYPE = cFWPM_CONDITION_IP_LOCAL_PORT
+var cFWPM_CONDITION_ICMP_CODE = cFWPM_CONDITION_IP_REMOTE_PORT
+
+// 7bc43cbf-37ba-45f1-b74a-82ff518eeb10
+var cFWPM_CONDITION_L2_FLAGS = windows.GUID{
+ Data1: 0x7bc43cbf,
+ Data2: 0x37ba,
+ Data3: 0x45f1,
+ Data4: [8]byte{0xb7, 0x4a, 0x82, 0xff, 0x51, 0x8e, 0xeb, 0x10},
+}
+
+type wtFwpmL2Flags uint32
+
+const cFWP_CONDITION_L2_IS_VM2VM wtFwpmL2Flags = 0x00000010
+
+// Defined in fwpmtypes.h
+type wtFwpmFilterFlags uint32
+
+const (
+ cFWPM_FILTER_FLAG_NONE wtFwpmFilterFlags = 0x00000000
+ cFWPM_FILTER_FLAG_PERSISTENT wtFwpmFilterFlags = 0x00000001
+ cFWPM_FILTER_FLAG_BOOTTIME wtFwpmFilterFlags = 0x00000002
+ cFWPM_FILTER_FLAG_HAS_PROVIDER_CONTEXT wtFwpmFilterFlags = 0x00000004
+ cFWPM_FILTER_FLAG_CLEAR_ACTION_RIGHT wtFwpmFilterFlags = 0x00000008
+ cFWPM_FILTER_FLAG_PERMIT_IF_CALLOUT_UNREGISTERED wtFwpmFilterFlags = 0x00000010
+ cFWPM_FILTER_FLAG_DISABLED wtFwpmFilterFlags = 0x00000020
+ cFWPM_FILTER_FLAG_INDEXED wtFwpmFilterFlags = 0x00000040
+ cFWPM_FILTER_FLAG_HAS_SECURITY_REALM_PROVIDER_CONTEXT wtFwpmFilterFlags = 0x00000080
+ cFWPM_FILTER_FLAG_SYSTEMOS_ONLY wtFwpmFilterFlags = 0x00000100
+ cFWPM_FILTER_FLAG_GAMEOS_ONLY wtFwpmFilterFlags = 0x00000200
+ cFWPM_FILTER_FLAG_SILENT_MODE wtFwpmFilterFlags = 0x00000400
+ cFWPM_FILTER_FLAG_IPSEC_NO_ACQUIRE_INITIATE wtFwpmFilterFlags = 0x00000800
+)
+
+// FWPM_LAYER_ALE_AUTH_CONNECT_V4 (c38d57d1-05a7-4c33-904f-7fbceee60e82) defined in fwpmu.h
+var cFWPM_LAYER_ALE_AUTH_CONNECT_V4 = windows.GUID{
+ Data1: 0xc38d57d1,
+ Data2: 0x05a7,
+ Data3: 0x4c33,
+ Data4: [8]byte{0x90, 0x4f, 0x7f, 0xbc, 0xee, 0xe6, 0x0e, 0x82},
+}
+
+// e1cd9fe7-f4b5-4273-96c0-592e487b8650
+var cFWPM_LAYER_ALE_AUTH_RECV_ACCEPT_V4 = windows.GUID{
+ Data1: 0xe1cd9fe7,
+ Data2: 0xf4b5,
+ Data3: 0x4273,
+ Data4: [8]byte{0x96, 0xc0, 0x59, 0x2e, 0x48, 0x7b, 0x86, 0x50},
+}
+
+// FWPM_LAYER_ALE_AUTH_CONNECT_V6 (4a72393b-319f-44bc-84c3-ba54dcb3b6b4) defined in fwpmu.h
+var cFWPM_LAYER_ALE_AUTH_CONNECT_V6 = windows.GUID{
+ Data1: 0x4a72393b,
+ Data2: 0x319f,
+ Data3: 0x44bc,
+ Data4: [8]byte{0x84, 0xc3, 0xba, 0x54, 0xdc, 0xb3, 0xb6, 0xb4},
+}
+
+// a3b42c97-9f04-4672-b87e-cee9c483257f
+var cFWPM_LAYER_ALE_AUTH_RECV_ACCEPT_V6 = windows.GUID{
+ Data1: 0xa3b42c97,
+ Data2: 0x9f04,
+ Data3: 0x4672,
+ Data4: [8]byte{0xb8, 0x7e, 0xce, 0xe9, 0xc4, 0x83, 0x25, 0x7f},
+}
+
+// 94c44912-9d6f-4ebf-b995-05ab8a088d1b
+var cFWPM_LAYER_OUTBOUND_MAC_FRAME_NATIVE = windows.GUID{
+ Data1: 0x94c44912,
+ Data2: 0x9d6f,
+ Data3: 0x4ebf,
+ Data4: [8]byte{0xb9, 0x95, 0x05, 0xab, 0x8a, 0x08, 0x8d, 0x1b},
+}
+
+// d4220bd3-62ce-4f08-ae88-b56e8526df50
+var cFWPM_LAYER_INBOUND_MAC_FRAME_NATIVE = windows.GUID{
+ Data1: 0xd4220bd3,
+ Data2: 0x62ce,
+ Data3: 0x4f08,
+ Data4: [8]byte{0xae, 0x88, 0xb5, 0x6e, 0x85, 0x26, 0xdf, 0x50},
+}
+
+// FWP_BITMAP_ARRAY64 defined in fwtypes.h
+type wtFwpBitmapArray64 struct {
+ bitmapArray64 [8]uint8 // Windows type: [8]UINT8
+}
+
+// FWP_BYTE_ARRAY6 defined in fwtypes.h
+// (https://docs.microsoft.com/en-us/windows/desktop/api/fwptypes/ns-fwptypes-fwp_byte_array6_)
+type wtFwpByteArray6 struct {
+ byteArray6 [6]uint8 // Windows type: [6]UINT8
+}
+
+// FWP_BYTE_ARRAY16 defined in fwptypes.h
+// (https://docs.microsoft.com/en-us/windows/desktop/api/fwptypes/ns-fwptypes-fwp_byte_array16_)
+type wtFwpByteArray16 struct {
+ byteArray16 [16]uint8 // Windows type [16]UINT8
+}
+
+// FWP_CONDITION_VALUE0 defined in fwptypes.h
+// (https://docs.microsoft.com/en-us/windows/desktop/api/fwptypes/ns-fwptypes-fwp_condition_value0_).
+type wtFwpConditionValue0 wtFwpValue0
+
+// FWP_DATA_TYPE defined in fwptypes.h
+// (https://docs.microsoft.com/en-us/windows/desktop/api/fwptypes/ne-fwptypes-fwp_data_type_)
+type wtFwpDataType uint
+
+const (
+ cFWP_EMPTY wtFwpDataType = 0
+ cFWP_UINT8 wtFwpDataType = cFWP_EMPTY + 1
+ cFWP_UINT16 wtFwpDataType = cFWP_UINT8 + 1
+ cFWP_UINT32 wtFwpDataType = cFWP_UINT16 + 1
+ cFWP_UINT64 wtFwpDataType = cFWP_UINT32 + 1
+ cFWP_INT8 wtFwpDataType = cFWP_UINT64 + 1
+ cFWP_INT16 wtFwpDataType = cFWP_INT8 + 1
+ cFWP_INT32 wtFwpDataType = cFWP_INT16 + 1
+ cFWP_INT64 wtFwpDataType = cFWP_INT32 + 1
+ cFWP_FLOAT wtFwpDataType = cFWP_INT64 + 1
+ cFWP_DOUBLE wtFwpDataType = cFWP_FLOAT + 1
+ cFWP_BYTE_ARRAY16_TYPE wtFwpDataType = cFWP_DOUBLE + 1
+ cFWP_BYTE_BLOB_TYPE wtFwpDataType = cFWP_BYTE_ARRAY16_TYPE + 1
+ cFWP_SID wtFwpDataType = cFWP_BYTE_BLOB_TYPE + 1
+ cFWP_SECURITY_DESCRIPTOR_TYPE wtFwpDataType = cFWP_SID + 1
+ cFWP_TOKEN_INFORMATION_TYPE wtFwpDataType = cFWP_SECURITY_DESCRIPTOR_TYPE + 1
+ cFWP_TOKEN_ACCESS_INFORMATION_TYPE wtFwpDataType = cFWP_TOKEN_INFORMATION_TYPE + 1
+ cFWP_UNICODE_STRING_TYPE wtFwpDataType = cFWP_TOKEN_ACCESS_INFORMATION_TYPE + 1
+ cFWP_BYTE_ARRAY6_TYPE wtFwpDataType = cFWP_UNICODE_STRING_TYPE + 1
+ cFWP_BITMAP_INDEX_TYPE wtFwpDataType = cFWP_BYTE_ARRAY6_TYPE + 1
+ cFWP_BITMAP_ARRAY64_TYPE wtFwpDataType = cFWP_BITMAP_INDEX_TYPE + 1
+ cFWP_SINGLE_DATA_TYPE_MAX wtFwpDataType = 0xff
+ cFWP_V4_ADDR_MASK wtFwpDataType = cFWP_SINGLE_DATA_TYPE_MAX + 1
+ cFWP_V6_ADDR_MASK wtFwpDataType = cFWP_V4_ADDR_MASK + 1
+ cFWP_RANGE_TYPE wtFwpDataType = cFWP_V6_ADDR_MASK + 1
+ cFWP_DATA_TYPE_MAX wtFwpDataType = cFWP_RANGE_TYPE + 1
+)
+
+// FWP_V4_ADDR_AND_MASK defined in fwptypes.h
+// (https://docs.microsoft.com/en-us/windows/desktop/api/fwptypes/ns-fwptypes-fwp_v4_addr_and_mask_).
+type wtFwpV4AddrAndMask struct {
+ addr uint32
+ mask uint32
+}
+
+// FWP_V6_ADDR_AND_MASK defined in fwptypes.h
+// (https://docs.microsoft.com/en-us/windows/desktop/api/fwptypes/ns-fwptypes-fwp_v6_addr_and_mask_).
+type wtFwpV6AddrAndMask struct {
+ addr [16]uint8
+ prefixLength uint8
+}
+
+// FWP_VALUE0 defined in fwptypes.h
+// (https://docs.microsoft.com/en-us/windows/desktop/api/fwptypes/ns-fwptypes-fwp_value0_)
+type wtFwpValue0 struct {
+ _type wtFwpDataType
+ value uintptr
+}
+
+// FWPM_DISPLAY_DATA0 defined in fwptypes.h
+// (https://docs.microsoft.com/en-us/windows/desktop/api/fwptypes/ns-fwptypes-fwpm_display_data0_).
+type wtFwpmDisplayData0 struct {
+ name *uint16 // Windows type: *wchar_t
+ description *uint16 // Windows type: *wchar_t
+}
+
+// FWPM_FILTER_CONDITION0 defined in fwpmtypes.h
+// (https://docs.microsoft.com/en-us/windows/desktop/api/fwpmtypes/ns-fwpmtypes-fwpm_filter_condition0_).
+type wtFwpmFilterCondition0 struct {
+ fieldKey windows.GUID // Windows type: GUID
+ matchType wtFwpMatchType
+ conditionValue wtFwpConditionValue0
+}
+
+// FWPM_PROVIDER0 defined in fwpmtypes.h
+// (https://docs.microsoft.com/en-us/windows/desktop/api/fwpmtypes/ns-fwpmtypes-fwpm_provider0_)
+type wtFwpProvider0 struct {
+ providerKey windows.GUID // Windows type: GUID
+ displayData wtFwpmDisplayData0
+ flags uint32
+ providerData wtFwpByteBlob
+ serviceName *uint16 // Windows type: *wchar_t
+}
+
+type wtFwpmSessionFlagsValue uint32
+
+const (
+ cFWPM_SESSION_FLAG_DYNAMIC wtFwpmSessionFlagsValue = 0x00000001 // FWPM_SESSION_FLAG_DYNAMIC defined in fwpmtypes.h
+)
+
+// FWPM_SESSION0 defined in fwpmtypes.h
+// (https://docs.microsoft.com/en-us/windows/desktop/api/fwpmtypes/ns-fwpmtypes-fwpm_session0_).
+type wtFwpmSession0 struct {
+ sessionKey windows.GUID // Windows type: GUID
+ displayData wtFwpmDisplayData0
+ flags wtFwpmSessionFlagsValue // Windows type UINT32
+ txnWaitTimeoutInMSec uint32
+ processId uint32 // Windows type: DWORD
+ sid *windows.SID
+ username *uint16 // Windows type: *wchar_t
+ kernelMode uint8 // Windows type: BOOL
+}
+
+type wtFwpmSublayerFlags uint32
+
+const (
+ cFWPM_SUBLAYER_FLAG_PERSISTENT wtFwpmSublayerFlags = 0x00000001 // FWPM_SUBLAYER_FLAG_PERSISTENT defined in fwpmtypes.h
+)
+
+// FWPM_SUBLAYER0 defined in fwpmtypes.h
+// (https://docs.microsoft.com/en-us/windows/desktop/api/fwpmtypes/ns-fwpmtypes-fwpm_sublayer0_)
+type wtFwpmSublayer0 struct {
+ subLayerKey windows.GUID // Windows type: GUID
+ displayData wtFwpmDisplayData0
+ flags wtFwpmSublayerFlags
+ providerKey *windows.GUID // Windows type: *GUID
+ providerData wtFwpByteBlob
+ weight uint16
+}
+
+// Defined in rpcdce.h
+type wtRpcCAuthN uint32
+
+const (
+ cRPC_C_AUTHN_NONE wtRpcCAuthN = 0
+ cRPC_C_AUTHN_WINNT wtRpcCAuthN = 10
+ cRPC_C_AUTHN_DEFAULT wtRpcCAuthN = 0xFFFFFFFF
+)
+
+// FWPM_PROVIDER0 defined in fwpmtypes.h
+// (https://docs.microsoft.com/sv-se/windows/desktop/api/fwpmtypes/ns-fwpmtypes-fwpm_provider0_).
+type wtFwpmProvider0 struct {
+ providerKey windows.GUID
+ displayData wtFwpmDisplayData0
+ flags uint32
+ providerData wtFwpByteBlob
+ serviceName *uint16
+}
+
+type wtObjectType uint32
+
+const (
+ cSE_KERNEL_OBJECT wtObjectType = 6
+
+ cDACL_SECURITY_INFORMATION = 4
+)
+
+type wtIfType uint32
+
+const (
+ cIF_TYPE_SOFTWARE_LOOPBACK wtIfType = 24
+)
+
+type wtIPProto uint32
+
+const (
+ cIPPROTO_ICMP wtIPProto = 1
+ cIPPROTO_ICMPV6 wtIPProto = 58
+ cIPPROTO_TCP wtIPProto = 6
+ cIPPROTO_UDP wtIPProto = 17
+)
diff --git a/tunnel/firewall/types_windows_386.go b/tunnel/firewall/types_windows_386.go
new file mode 100644
index 00000000..00d7ba8f
--- /dev/null
+++ b/tunnel/firewall/types_windows_386.go
@@ -0,0 +1,88 @@
+/* SPDX-License-Identifier: MIT
+ *
+ * Copyright (C) 2019 WireGuard LLC. All Rights Reserved.
+ */
+
+package firewall
+
+import "golang.org/x/sys/windows"
+
+const (
+ wtFwpByteBlob_Size = 8
+ wtFwpByteBlob_data_Offset = 4
+
+ wtFwpConditionValue0_Size = 8
+ wtFwpConditionValue0_uint8_Offset = 4
+
+ wtFwpmDisplayData0_Size = 8
+ wtFwpmDisplayData0_description_Offset = 4
+
+ wtFwpmFilter0_Size = 152
+ wtFwpmFilter0_displayData_Offset = 16
+ wtFwpmFilter0_flags_Offset = 24
+ wtFwpmFilter0_providerKey_Offset = 28
+ wtFwpmFilter0_providerData_Offset = 32
+ wtFwpmFilter0_layerKey_Offset = 40
+ wtFwpmFilter0_subLayerKey_Offset = 56
+ wtFwpmFilter0_weight_Offset = 72
+ wtFwpmFilter0_numFilterConditions_Offset = 80
+ wtFwpmFilter0_filterCondition_Offset = 84
+ wtFwpmFilter0_action_Offset = 88
+ wtFwpmFilter0_providerContextKey_Offset = 112
+ wtFwpmFilter0_reserved_Offset = 128
+ wtFwpmFilter0_filterID_Offset = 136
+ wtFwpmFilter0_effectiveWeight_Offset = 144
+
+ wtFwpmFilterCondition0_Size = 28
+ wtFwpmFilterCondition0_matchType_Offset = 16
+ wtFwpmFilterCondition0_conditionValue_Offset = 20
+
+ wtFwpmSession0_Size = 48
+ wtFwpmSession0_displayData_Offset = 16
+ wtFwpmSession0_flags_Offset = 24
+ wtFwpmSession0_txnWaitTimeoutInMSec_Offset = 28
+ wtFwpmSession0_processId_Offset = 32
+ wtFwpmSession0_sid_Offset = 36
+ wtFwpmSession0_username_Offset = 40
+ wtFwpmSession0_kernelMode_Offset = 44
+
+ wtFwpmSublayer0_Size = 44
+ wtFwpmSublayer0_displayData_Offset = 16
+ wtFwpmSublayer0_flags_Offset = 24
+ wtFwpmSublayer0_providerKey_Offset = 28
+ wtFwpmSublayer0_providerData_Offset = 32
+ wtFwpmSublayer0_weight_Offset = 40
+
+ wtFwpProvider0_Size = 40
+ wtFwpProvider0_displayData_Offset = 16
+ wtFwpProvider0_flags_Offset = 24
+ wtFwpProvider0_providerData_Offset = 28
+ wtFwpProvider0_serviceName_Offset = 36
+
+ wtFwpTokenInformation_Size = 16
+
+ wtFwpValue0_Size = 8
+ wtFwpValue0_value_Offset = 4
+)
+
+// FWPM_FILTER0 defined in fwpmtypes.h
+// (https://docs.microsoft.com/en-us/windows/desktop/api/fwpmtypes/ns-fwpmtypes-fwpm_filter0_).
+type wtFwpmFilter0 struct {
+ filterKey windows.GUID // Windows type: GUID
+ displayData wtFwpmDisplayData0
+ flags wtFwpmFilterFlags
+ providerKey *windows.GUID // Windows type: *GUID
+ providerData wtFwpByteBlob
+ layerKey windows.GUID // Windows type: GUID
+ subLayerKey windows.GUID // Windows type: GUID
+ weight wtFwpValue0
+ numFilterConditions uint32
+ filterCondition *wtFwpmFilterCondition0
+ action wtFwpmAction0
+ offset1 [4]byte // Layout correction field
+ providerContextKey windows.GUID // Windows type: GUID
+ reserved *windows.GUID // Windows type: *GUID
+ offset2 [4]byte // Layout correction field
+ filterID uint64
+ effectiveWeight wtFwpValue0
+}
diff --git a/tunnel/firewall/types_windows_amd64.go b/tunnel/firewall/types_windows_amd64.go
new file mode 100644
index 00000000..f0aa557c
--- /dev/null
+++ b/tunnel/firewall/types_windows_amd64.go
@@ -0,0 +1,85 @@
+/* SPDX-License-Identifier: MIT
+ *
+ * Copyright (C) 2019 WireGuard LLC. All Rights Reserved.
+ */
+
+package firewall
+
+import "golang.org/x/sys/windows"
+
+const (
+ wtFwpByteBlob_Size = 16
+ wtFwpByteBlob_data_Offset = 8
+
+ wtFwpConditionValue0_Size = 16
+ wtFwpConditionValue0_uint8_Offset = 8
+
+ wtFwpmDisplayData0_Size = 16
+ wtFwpmDisplayData0_description_Offset = 8
+
+ wtFwpmFilter0_Size = 200
+ wtFwpmFilter0_displayData_Offset = 16
+ wtFwpmFilter0_flags_Offset = 32
+ wtFwpmFilter0_providerKey_Offset = 40
+ wtFwpmFilter0_providerData_Offset = 48
+ wtFwpmFilter0_layerKey_Offset = 64
+ wtFwpmFilter0_subLayerKey_Offset = 80
+ wtFwpmFilter0_weight_Offset = 96
+ wtFwpmFilter0_numFilterConditions_Offset = 112
+ wtFwpmFilter0_filterCondition_Offset = 120
+ wtFwpmFilter0_action_Offset = 128
+ wtFwpmFilter0_providerContextKey_Offset = 152
+ wtFwpmFilter0_reserved_Offset = 168
+ wtFwpmFilter0_filterID_Offset = 176
+ wtFwpmFilter0_effectiveWeight_Offset = 184
+
+ wtFwpmFilterCondition0_Size = 40
+ wtFwpmFilterCondition0_matchType_Offset = 16
+ wtFwpmFilterCondition0_conditionValue_Offset = 24
+
+ wtFwpmSession0_Size = 72
+ wtFwpmSession0_displayData_Offset = 16
+ wtFwpmSession0_flags_Offset = 32
+ wtFwpmSession0_txnWaitTimeoutInMSec_Offset = 36
+ wtFwpmSession0_processId_Offset = 40
+ wtFwpmSession0_sid_Offset = 48
+ wtFwpmSession0_username_Offset = 56
+ wtFwpmSession0_kernelMode_Offset = 64
+
+ wtFwpmSublayer0_Size = 72
+ wtFwpmSublayer0_displayData_Offset = 16
+ wtFwpmSublayer0_flags_Offset = 32
+ wtFwpmSublayer0_providerKey_Offset = 40
+ wtFwpmSublayer0_providerData_Offset = 48
+ wtFwpmSublayer0_weight_Offset = 64
+
+ wtFwpProvider0_Size = 64
+ wtFwpProvider0_displayData_Offset = 16
+ wtFwpProvider0_flags_Offset = 32
+ wtFwpProvider0_providerData_Offset = 40
+ wtFwpProvider0_serviceName_Offset = 56
+
+ wtFwpValue0_Size = 16
+ wtFwpValue0_value_Offset = 8
+)
+
+// FWPM_FILTER0 defined in fwpmtypes.h
+// (https://docs.microsoft.com/en-us/windows/desktop/api/fwpmtypes/ns-fwpmtypes-fwpm_filter0_).
+type wtFwpmFilter0 struct {
+ filterKey windows.GUID // Windows type: GUID
+ displayData wtFwpmDisplayData0
+ flags wtFwpmFilterFlags // Windows type: UINT32
+ providerKey *windows.GUID // Windows type: *GUID
+ providerData wtFwpByteBlob
+ layerKey windows.GUID // Windows type: GUID
+ subLayerKey windows.GUID // Windows type: GUID
+ weight wtFwpValue0
+ numFilterConditions uint32
+ filterCondition *wtFwpmFilterCondition0
+ action wtFwpmAction0
+ offset1 [4]byte // Layout correction field
+ providerContextKey windows.GUID // Windows type: GUID
+ reserved *windows.GUID // Windows type: *GUID
+ filterID uint64
+ effectiveWeight wtFwpValue0
+}
diff --git a/tunnel/firewall/types_windows_test.go b/tunnel/firewall/types_windows_test.go
new file mode 100644
index 00000000..97cb032c
--- /dev/null
+++ b/tunnel/firewall/types_windows_test.go
@@ -0,0 +1,538 @@
+/* SPDX-License-Identifier: MIT
+ *
+ * Copyright (C) 2019 WireGuard LLC. All Rights Reserved.
+ */
+
+package firewall
+
+import (
+ "testing"
+ "unsafe"
+)
+
+func TestWtFwpByteBlobSize(t *testing.T) {
+
+ const actualWtFwpByteBlobSize = unsafe.Sizeof(wtFwpByteBlob{})
+
+ if actualWtFwpByteBlobSize != wtFwpByteBlob_Size {
+ t.Errorf("Size of FwpByteBlob is %d, although %d is expected.", actualWtFwpByteBlobSize,
+ wtFwpByteBlob_Size)
+ }
+}
+
+func TestWtFwpByteBlobOffsets(t *testing.T) {
+
+ s := wtFwpByteBlob{}
+ sp := uintptr(unsafe.Pointer(&s))
+
+ offset := uintptr(unsafe.Pointer(&s.data)) - sp
+
+ if offset != wtFwpByteBlob_data_Offset {
+ t.Errorf("FwpByteBlob.data offset is %d although %d is expected", offset, wtFwpByteBlob_data_Offset)
+ return
+ }
+}
+
+func TestWtFwpmAction0Size(t *testing.T) {
+
+ const actualWtFwpmAction0Size = unsafe.Sizeof(wtFwpmAction0{})
+
+ if actualWtFwpmAction0Size != wtFwpmAction0_Size {
+ t.Errorf("Size of wtFwpmAction0 is %d, although %d is expected.", actualWtFwpmAction0Size,
+ wtFwpmAction0_Size)
+ }
+}
+
+func TestWtFwpmAction0Offsets(t *testing.T) {
+
+ s := wtFwpmAction0{}
+ sp := uintptr(unsafe.Pointer(&s))
+
+ offset := uintptr(unsafe.Pointer(&s.filterType)) - sp
+
+ if offset != wtFwpmAction0_filterType_Offset {
+ t.Errorf("wtFwpmAction0.filterType offset is %d although %d is expected", offset,
+ wtFwpmAction0_filterType_Offset)
+ return
+ }
+}
+
+func TestWtFwpBitmapArray64Size(t *testing.T) {
+
+ const actualWtFwpBitmapArray64Size = unsafe.Sizeof(wtFwpBitmapArray64{})
+
+ if actualWtFwpBitmapArray64Size != wtFwpBitmapArray64_Size {
+ t.Errorf("Size of wtFwpBitmapArray64 is %d, although %d is expected.", actualWtFwpBitmapArray64Size,
+ wtFwpBitmapArray64_Size)
+ }
+}
+
+func TestWtFwpByteArray6Size(t *testing.T) {
+
+ const actualWtFwpByteArray6Size = unsafe.Sizeof(wtFwpByteArray6{})
+
+ if actualWtFwpByteArray6Size != wtFwpByteArray6_Size {
+ t.Errorf("Size of wtFwpByteArray6 is %d, although %d is expected.", actualWtFwpByteArray6Size,
+ wtFwpByteArray6_Size)
+ }
+}
+
+func TestWtFwpByteArray16Size(t *testing.T) {
+
+ const actualWtFwpByteArray16Size = unsafe.Sizeof(wtFwpByteArray16{})
+
+ if actualWtFwpByteArray16Size != wtFwpByteArray16_Size {
+ t.Errorf("Size of wtFwpByteArray16 is %d, although %d is expected.", actualWtFwpByteArray16Size,
+ wtFwpByteArray16_Size)
+ }
+}
+
+func TestWtFwpConditionValue0Size(t *testing.T) {
+
+ const actualWtFwpConditionValue0Size = unsafe.Sizeof(wtFwpConditionValue0{})
+
+ if actualWtFwpConditionValue0Size != wtFwpConditionValue0_Size {
+ t.Errorf("Size of wtFwpConditionValue0 is %d, although %d is expected.", actualWtFwpConditionValue0Size,
+ wtFwpConditionValue0_Size)
+ }
+}
+
+func TestWtFwpConditionValue0Offsets(t *testing.T) {
+
+ s := wtFwpConditionValue0{}
+ sp := uintptr(unsafe.Pointer(&s))
+
+ offset := uintptr(unsafe.Pointer(&s.value)) - sp
+
+ if offset != wtFwpConditionValue0_uint8_Offset {
+ t.Errorf("wtFwpConditionValue0.value offset is %d although %d is expected", offset, wtFwpConditionValue0_uint8_Offset)
+ return
+ }
+}
+
+func TestWtFwpV4AddrAndMaskSize(t *testing.T) {
+
+ const actualWtFwpV4AddrAndMaskSize = unsafe.Sizeof(wtFwpV4AddrAndMask{})
+
+ if actualWtFwpV4AddrAndMaskSize != wtFwpV4AddrAndMask_Size {
+ t.Errorf("Size of wtFwpV4AddrAndMask is %d, although %d is expected.", actualWtFwpV4AddrAndMaskSize,
+ wtFwpV4AddrAndMask_Size)
+ }
+}
+
+func TestWtFwpV4AddrAndMaskOffsets(t *testing.T) {
+
+ s := wtFwpV4AddrAndMask{}
+ sp := uintptr(unsafe.Pointer(&s))
+
+ offset := uintptr(unsafe.Pointer(&s.mask)) - sp
+
+ if offset != wtFwpV4AddrAndMask_mask_Offset {
+ t.Errorf("wtFwpV4AddrAndMask.mask offset is %d although %d is expected", offset,
+ wtFwpV4AddrAndMask_mask_Offset)
+ return
+ }
+}
+
+func TestWtFwpV6AddrAndMaskSize(t *testing.T) {
+
+ const actualWtFwpV6AddrAndMaskSize = unsafe.Sizeof(wtFwpV6AddrAndMask{})
+
+ if actualWtFwpV6AddrAndMaskSize != wtFwpV6AddrAndMask_Size {
+ t.Errorf("Size of wtFwpV6AddrAndMask is %d, although %d is expected.", actualWtFwpV6AddrAndMaskSize,
+ wtFwpV6AddrAndMask_Size)
+ }
+}
+
+func TestWtFwpV6AddrAndMaskOffsets(t *testing.T) {
+
+ s := wtFwpV6AddrAndMask{}
+ sp := uintptr(unsafe.Pointer(&s))
+
+ offset := uintptr(unsafe.Pointer(&s.prefixLength)) - sp
+
+ if offset != wtFwpV6AddrAndMask_prefixLength_Offset {
+ t.Errorf("wtFwpV6AddrAndMask.prefixLength offset is %d although %d is expected", offset,
+ wtFwpV6AddrAndMask_prefixLength_Offset)
+ return
+ }
+}
+
+func TestWtFwpValue0Size(t *testing.T) {
+
+ const actualWtFwpValue0Size = unsafe.Sizeof(wtFwpValue0{})
+
+ if actualWtFwpValue0Size != wtFwpValue0_Size {
+ t.Errorf("Size of wtFwpValue0 is %d, although %d is expected.", actualWtFwpValue0Size, wtFwpValue0_Size)
+ }
+}
+
+func TestWtFwpValue0Offsets(t *testing.T) {
+
+ s := wtFwpValue0{}
+ sp := uintptr(unsafe.Pointer(&s))
+
+ offset := uintptr(unsafe.Pointer(&s.value)) - sp
+
+ if offset != wtFwpValue0_value_Offset {
+ t.Errorf("wtFwpValue0.value offset is %d although %d is expected", offset, wtFwpValue0_value_Offset)
+ return
+ }
+}
+
+func TestWtFwpmDisplayData0Size(t *testing.T) {
+
+ const actualWtFwpmDisplayData0Size = unsafe.Sizeof(wtFwpmDisplayData0{})
+
+ if actualWtFwpmDisplayData0Size != wtFwpmDisplayData0_Size {
+ t.Errorf("Size of wtFwpmDisplayData0 is %d, although %d is expected.", actualWtFwpmDisplayData0Size,
+ wtFwpmDisplayData0_Size)
+ }
+}
+
+func TestWtFwpmDisplayData0Offsets(t *testing.T) {
+
+ s := wtFwpmDisplayData0{}
+ sp := uintptr(unsafe.Pointer(&s))
+
+ offset := uintptr(unsafe.Pointer(&s.description)) - sp
+
+ if offset != wtFwpmDisplayData0_description_Offset {
+ t.Errorf("wtFwpmDisplayData0.description offset is %d although %d is expected", offset,
+ wtFwpmDisplayData0_description_Offset)
+ return
+ }
+}
+
+func TestWtFwpmFilterCondition0Size(t *testing.T) {
+
+ const actualWtFwpmFilterCondition0Size = unsafe.Sizeof(wtFwpmFilterCondition0{})
+
+ if actualWtFwpmFilterCondition0Size != wtFwpmFilterCondition0_Size {
+ t.Errorf("Size of wtFwpmFilterCondition0 is %d, although %d is expected.",
+ actualWtFwpmFilterCondition0Size, wtFwpmFilterCondition0_Size)
+ }
+}
+
+func TestWtFwpmFilterCondition0Offsets(t *testing.T) {
+
+ s := wtFwpmFilterCondition0{}
+ sp := uintptr(unsafe.Pointer(&s))
+
+ offset := uintptr(unsafe.Pointer(&s.matchType)) - sp
+
+ if offset != wtFwpmFilterCondition0_matchType_Offset {
+ t.Errorf("wtFwpmFilterCondition0.matchType offset is %d although %d is expected", offset,
+ wtFwpmFilterCondition0_matchType_Offset)
+ return
+ }
+
+ offset = uintptr(unsafe.Pointer(&s.conditionValue)) - sp
+
+ if offset != wtFwpmFilterCondition0_conditionValue_Offset {
+ t.Errorf("wtFwpmFilterCondition0.conditionValue offset is %d although %d is expected", offset,
+ wtFwpmFilterCondition0_conditionValue_Offset)
+ return
+ }
+}
+
+func TestWtFwpmFilter0Size(t *testing.T) {
+
+ const actualWtFwpmFilter0Size = unsafe.Sizeof(wtFwpmFilter0{})
+
+ if actualWtFwpmFilter0Size != wtFwpmFilter0_Size {
+ t.Errorf("Size of wtFwpmFilter0 is %d, although %d is expected.", actualWtFwpmFilter0Size,
+ wtFwpmFilter0_Size)
+ }
+}
+
+func TestWtFwpmFilter0Offsets(t *testing.T) {
+
+ s := wtFwpmFilter0{}
+ sp := uintptr(unsafe.Pointer(&s))
+
+ offset := uintptr(unsafe.Pointer(&s.displayData)) - sp
+
+ if offset != wtFwpmFilter0_displayData_Offset {
+ t.Errorf("wtFwpmFilter0.displayData offset is %d although %d is expected", offset,
+ wtFwpmFilter0_displayData_Offset)
+ return
+ }
+
+ offset = uintptr(unsafe.Pointer(&s.flags)) - sp
+
+ if offset != wtFwpmFilter0_flags_Offset {
+ t.Errorf("wtFwpmFilter0.flags offset is %d although %d is expected", offset, wtFwpmFilter0_flags_Offset)
+ return
+ }
+
+ offset = uintptr(unsafe.Pointer(&s.providerKey)) - sp
+
+ if offset != wtFwpmFilter0_providerKey_Offset {
+ t.Errorf("wtFwpmFilter0.providerKey offset is %d although %d is expected", offset,
+ wtFwpmFilter0_providerKey_Offset)
+ return
+ }
+
+ offset = uintptr(unsafe.Pointer(&s.providerData)) - sp
+
+ if offset != wtFwpmFilter0_providerData_Offset {
+ t.Errorf("wtFwpmFilter0.providerData offset is %d although %d is expected", offset,
+ wtFwpmFilter0_providerData_Offset)
+ return
+ }
+
+ offset = uintptr(unsafe.Pointer(&s.layerKey)) - sp
+
+ if offset != wtFwpmFilter0_layerKey_Offset {
+ t.Errorf("wtFwpmFilter0.layerKey offset is %d although %d is expected", offset,
+ wtFwpmFilter0_layerKey_Offset)
+ return
+ }
+
+ offset = uintptr(unsafe.Pointer(&s.subLayerKey)) - sp
+
+ if offset != wtFwpmFilter0_subLayerKey_Offset {
+ t.Errorf("wtFwpmFilter0.subLayerKey offset is %d although %d is expected", offset,
+ wtFwpmFilter0_subLayerKey_Offset)
+ return
+ }
+
+ offset = uintptr(unsafe.Pointer(&s.weight)) - sp
+
+ if offset != wtFwpmFilter0_weight_Offset {
+ t.Errorf("wtFwpmFilter0.weight offset is %d although %d is expected", offset,
+ wtFwpmFilter0_weight_Offset)
+ return
+ }
+
+ offset = uintptr(unsafe.Pointer(&s.numFilterConditions)) - sp
+
+ if offset != wtFwpmFilter0_numFilterConditions_Offset {
+ t.Errorf("wtFwpmFilter0.numFilterConditions offset is %d although %d is expected", offset,
+ wtFwpmFilter0_numFilterConditions_Offset)
+ return
+ }
+
+ offset = uintptr(unsafe.Pointer(&s.filterCondition)) - sp
+
+ if offset != wtFwpmFilter0_filterCondition_Offset {
+ t.Errorf("wtFwpmFilter0.filterCondition offset is %d although %d is expected", offset,
+ wtFwpmFilter0_filterCondition_Offset)
+ return
+ }
+
+ offset = uintptr(unsafe.Pointer(&s.action)) - sp
+
+ if offset != wtFwpmFilter0_action_Offset {
+ t.Errorf("wtFwpmFilter0.action offset is %d although %d is expected", offset,
+ wtFwpmFilter0_action_Offset)
+ return
+ }
+
+ offset = uintptr(unsafe.Pointer(&s.providerContextKey)) - sp
+
+ if offset != wtFwpmFilter0_providerContextKey_Offset {
+ t.Errorf("wtFwpmFilter0.providerContextKey offset is %d although %d is expected", offset,
+ wtFwpmFilter0_providerContextKey_Offset)
+ return
+ }
+
+ offset = uintptr(unsafe.Pointer(&s.reserved)) - sp
+
+ if offset != wtFwpmFilter0_reserved_Offset {
+ t.Errorf("wtFwpmFilter0.reserved offset is %d although %d is expected", offset,
+ wtFwpmFilter0_reserved_Offset)
+ return
+ }
+
+ offset = uintptr(unsafe.Pointer(&s.filterID)) - sp
+
+ if offset != wtFwpmFilter0_filterID_Offset {
+ t.Errorf("wtFwpmFilter0.filterID offset is %d although %d is expected", offset,
+ wtFwpmFilter0_filterID_Offset)
+ return
+ }
+
+ offset = uintptr(unsafe.Pointer(&s.effectiveWeight)) - sp
+
+ if offset != wtFwpmFilter0_effectiveWeight_Offset {
+ t.Errorf("wtFwpmFilter0.effectiveWeight offset is %d although %d is expected", offset,
+ wtFwpmFilter0_effectiveWeight_Offset)
+ return
+ }
+}
+
+func TestWtFwpProvider0Size(t *testing.T) {
+
+ const actualWtFwpProvider0Size = unsafe.Sizeof(wtFwpProvider0{})
+
+ if actualWtFwpProvider0Size != wtFwpProvider0_Size {
+ t.Errorf("Size of wtFwpProvider0 is %d, although %d is expected.", actualWtFwpProvider0Size,
+ wtFwpProvider0_Size)
+ }
+}
+
+func TestWtFwpProvider0Offsets(t *testing.T) {
+
+ s := wtFwpProvider0{}
+ sp := uintptr(unsafe.Pointer(&s))
+
+ offset := uintptr(unsafe.Pointer(&s.displayData)) - sp
+
+ if offset != wtFwpProvider0_displayData_Offset {
+ t.Errorf("wtFwpProvider0.displayData offset is %d although %d is expected", offset,
+ wtFwpProvider0_displayData_Offset)
+ return
+ }
+
+ offset = uintptr(unsafe.Pointer(&s.flags)) - sp
+
+ if offset != wtFwpProvider0_flags_Offset {
+ t.Errorf("wtFwpProvider0.flags offset is %d although %d is expected", offset,
+ wtFwpProvider0_flags_Offset)
+ return
+ }
+
+ offset = uintptr(unsafe.Pointer(&s.providerData)) - sp
+
+ if offset != wtFwpProvider0_providerData_Offset {
+ t.Errorf("wtFwpProvider0.providerData offset is %d although %d is expected", offset,
+ wtFwpProvider0_providerData_Offset)
+ return
+ }
+
+ offset = uintptr(unsafe.Pointer(&s.serviceName)) - sp
+
+ if offset != wtFwpProvider0_serviceName_Offset {
+ t.Errorf("wtFwpProvider0.serviceName offset is %d although %d is expected", offset,
+ wtFwpProvider0_serviceName_Offset)
+ return
+ }
+}
+
+func TestWtFwpmSession0Size(t *testing.T) {
+
+ const actualWtFwpmSession0Size = unsafe.Sizeof(wtFwpmSession0{})
+
+ if actualWtFwpmSession0Size != wtFwpmSession0_Size {
+ t.Errorf("Size of wtFwpmSession0 is %d, although %d is expected.", actualWtFwpmSession0Size,
+ wtFwpmSession0_Size)
+ }
+}
+
+func TestWtFwpmSession0Offsets(t *testing.T) {
+
+ s := wtFwpmSession0{}
+ sp := uintptr(unsafe.Pointer(&s))
+
+ offset := uintptr(unsafe.Pointer(&s.displayData)) - sp
+
+ if offset != wtFwpmSession0_displayData_Offset {
+ t.Errorf("wtFwpmSession0.displayData offset is %d although %d is expected", offset,
+ wtFwpmSession0_displayData_Offset)
+ return
+ }
+
+ offset = uintptr(unsafe.Pointer(&s.flags)) - sp
+
+ if offset != wtFwpmSession0_flags_Offset {
+ t.Errorf("wtFwpmSession0.flags offset is %d although %d is expected", offset, wtFwpmSession0_flags_Offset)
+ return
+ }
+
+ offset = uintptr(unsafe.Pointer(&s.txnWaitTimeoutInMSec)) - sp
+
+ if offset != wtFwpmSession0_txnWaitTimeoutInMSec_Offset {
+ t.Errorf("wtFwpmSession0.txnWaitTimeoutInMSec offset is %d although %d is expected", offset,
+ wtFwpmSession0_txnWaitTimeoutInMSec_Offset)
+ return
+ }
+
+ offset = uintptr(unsafe.Pointer(&s.processId)) - sp
+
+ if offset != wtFwpmSession0_processId_Offset {
+ t.Errorf("wtFwpmSession0.processId offset is %d although %d is expected", offset,
+ wtFwpmSession0_processId_Offset)
+ return
+ }
+
+ offset = uintptr(unsafe.Pointer(&s.sid)) - sp
+
+ if offset != wtFwpmSession0_sid_Offset {
+ t.Errorf("wtFwpmSession0.sid offset is %d although %d is expected", offset, wtFwpmSession0_sid_Offset)
+ return
+ }
+
+ offset = uintptr(unsafe.Pointer(&s.username)) - sp
+
+ if offset != wtFwpmSession0_username_Offset {
+ t.Errorf("wtFwpmSession0.username offset is %d although %d is expected", offset,
+ wtFwpmSession0_username_Offset)
+ return
+ }
+
+ offset = uintptr(unsafe.Pointer(&s.kernelMode)) - sp
+
+ if offset != wtFwpmSession0_kernelMode_Offset {
+ t.Errorf("wtFwpmSession0.kernelMode offset is %d although %d is expected", offset,
+ wtFwpmSession0_kernelMode_Offset)
+ return
+ }
+}
+
+func TestWtFwpmSublayer0Size(t *testing.T) {
+
+ const actualWtFwpmSublayer0Size = unsafe.Sizeof(wtFwpmSublayer0{})
+
+ if actualWtFwpmSublayer0Size != wtFwpmSublayer0_Size {
+ t.Errorf("Size of wtFwpmSublayer0 is %d, although %d is expected.", actualWtFwpmSublayer0Size,
+ wtFwpmSublayer0_Size)
+ }
+}
+
+func TestWtFwpmSublayer0Offsets(t *testing.T) {
+
+ s := wtFwpmSublayer0{}
+ sp := uintptr(unsafe.Pointer(&s))
+
+ offset := uintptr(unsafe.Pointer(&s.displayData)) - sp
+
+ if offset != wtFwpmSublayer0_displayData_Offset {
+ t.Errorf("wtFwpmSublayer0.displayData offset is %d although %d is expected", offset,
+ wtFwpmSublayer0_displayData_Offset)
+ return
+ }
+
+ offset = uintptr(unsafe.Pointer(&s.flags)) - sp
+
+ if offset != wtFwpmSublayer0_flags_Offset {
+ t.Errorf("wtFwpmSublayer0.flags offset is %d although %d is expected", offset,
+ wtFwpmSublayer0_flags_Offset)
+ return
+ }
+
+ offset = uintptr(unsafe.Pointer(&s.providerKey)) - sp
+
+ if offset != wtFwpmSublayer0_providerKey_Offset {
+ t.Errorf("wtFwpmSublayer0.providerKey offset is %d although %d is expected", offset,
+ wtFwpmSublayer0_providerKey_Offset)
+ return
+ }
+
+ offset = uintptr(unsafe.Pointer(&s.providerData)) - sp
+
+ if offset != wtFwpmSublayer0_providerData_Offset {
+ t.Errorf("wtFwpmSublayer0.providerData offset is %d although %d is expected", offset,
+ wtFwpmSublayer0_providerData_Offset)
+ return
+ }
+
+ offset = uintptr(unsafe.Pointer(&s.weight)) - sp
+
+ if offset != wtFwpmSublayer0_weight_Offset {
+ t.Errorf("wtFwpmSublayer0.weight offset is %d although %d is expected", offset,
+ wtFwpmSublayer0_weight_Offset)
+ return
+ }
+}
diff --git a/tunnel/firewall/zsyscall_windows.go b/tunnel/firewall/zsyscall_windows.go
new file mode 100644
index 00000000..15e72703
--- /dev/null
+++ b/tunnel/firewall/zsyscall_windows.go
@@ -0,0 +1,186 @@
+// Code generated by 'go generate'; DO NOT EDIT.
+
+package firewall
+
+import (
+ "syscall"
+ "unsafe"
+
+ "golang.org/x/sys/windows"
+)
+
+var _ unsafe.Pointer
+
+// Do the interface allocations only once for common
+// Errno values.
+const (
+ errnoERROR_IO_PENDING = 997
+)
+
+var (
+ errERROR_IO_PENDING error = syscall.Errno(errnoERROR_IO_PENDING)
+)
+
+// errnoErr returns common boxed Errno values, to prevent
+// allocations at runtime.
+func errnoErr(e syscall.Errno) error {
+ switch e {
+ case 0:
+ return nil
+ case errnoERROR_IO_PENDING:
+ return errERROR_IO_PENDING
+ }
+ // TODO: add more here, after collecting data on the common
+ // error values see on Windows. (perhaps when running
+ // all.bat?)
+ return e
+}
+
+var (
+ modfwpuclnt = windows.NewLazySystemDLL("fwpuclnt.dll")
+ modadvapi32 = windows.NewLazySystemDLL("advapi32.dll")
+
+ procFwpmEngineOpen0 = modfwpuclnt.NewProc("FwpmEngineOpen0")
+ procFwpmEngineClose0 = modfwpuclnt.NewProc("FwpmEngineClose0")
+ procFwpmSubLayerAdd0 = modfwpuclnt.NewProc("FwpmSubLayerAdd0")
+ procFwpmGetAppIdFromFileName0 = modfwpuclnt.NewProc("FwpmGetAppIdFromFileName0")
+ procFwpmFreeMemory0 = modfwpuclnt.NewProc("FwpmFreeMemory0")
+ procFwpmFilterAdd0 = modfwpuclnt.NewProc("FwpmFilterAdd0")
+ procFwpmTransactionBegin0 = modfwpuclnt.NewProc("FwpmTransactionBegin0")
+ procFwpmTransactionCommit0 = modfwpuclnt.NewProc("FwpmTransactionCommit0")
+ procFwpmTransactionAbort0 = modfwpuclnt.NewProc("FwpmTransactionAbort0")
+ procFwpmProviderAdd0 = modfwpuclnt.NewProc("FwpmProviderAdd0")
+ procGetSecurityInfo = modadvapi32.NewProc("GetSecurityInfo")
+ procGetSecurityDescriptorLength = modadvapi32.NewProc("GetSecurityDescriptorLength")
+)
+
+func fwpmEngineOpen0(serverName *uint16, authnService wtRpcCAuthN, authIdentity *uintptr, session *wtFwpmSession0, engineHandle unsafe.Pointer) (err error) {
+ r1, _, e1 := syscall.Syscall6(procFwpmEngineOpen0.Addr(), 5, uintptr(unsafe.Pointer(serverName)), uintptr(authnService), uintptr(unsafe.Pointer(authIdentity)), uintptr(unsafe.Pointer(session)), uintptr(engineHandle), 0)
+ if r1 != 0 {
+ if e1 != 0 {
+ err = errnoErr(e1)
+ } else {
+ err = syscall.EINVAL
+ }
+ }
+ return
+}
+
+func fwpmEngineClose0(engineHandle uintptr) (err error) {
+ r1, _, e1 := syscall.Syscall(procFwpmEngineClose0.Addr(), 1, uintptr(engineHandle), 0, 0)
+ if r1 != 0 {
+ if e1 != 0 {
+ err = errnoErr(e1)
+ } else {
+ err = syscall.EINVAL
+ }
+ }
+ return
+}
+
+func fwpmSubLayerAdd0(engineHandle uintptr, subLayer *wtFwpmSublayer0, sd uintptr) (err error) {
+ r1, _, e1 := syscall.Syscall(procFwpmSubLayerAdd0.Addr(), 3, uintptr(engineHandle), uintptr(unsafe.Pointer(subLayer)), uintptr(sd))
+ if r1 != 0 {
+ if e1 != 0 {
+ err = errnoErr(e1)
+ } else {
+ err = syscall.EINVAL
+ }
+ }
+ return
+}
+
+func fwpmGetAppIdFromFileName0(fileName *uint16, appID unsafe.Pointer) (err error) {
+ r1, _, e1 := syscall.Syscall(procFwpmGetAppIdFromFileName0.Addr(), 2, uintptr(unsafe.Pointer(fileName)), uintptr(appID), 0)
+ if r1 != 0 {
+ if e1 != 0 {
+ err = errnoErr(e1)
+ } else {
+ err = syscall.EINVAL
+ }
+ }
+ return
+}
+
+func fwpmFreeMemory0(p unsafe.Pointer) {
+ syscall.Syscall(procFwpmFreeMemory0.Addr(), 1, uintptr(p), 0, 0)
+ return
+}
+
+func fwpmFilterAdd0(engineHandle uintptr, filter *wtFwpmFilter0, sd uintptr, id *uint64) (err error) {
+ r1, _, e1 := syscall.Syscall6(procFwpmFilterAdd0.Addr(), 4, uintptr(engineHandle), uintptr(unsafe.Pointer(filter)), uintptr(sd), uintptr(unsafe.Pointer(id)), 0, 0)
+ if r1 != 0 {
+ if e1 != 0 {
+ err = errnoErr(e1)
+ } else {
+ err = syscall.EINVAL
+ }
+ }
+ return
+}
+
+func fwpmTransactionBegin0(engineHandle uintptr, flags uint32) (err error) {
+ r1, _, e1 := syscall.Syscall(procFwpmTransactionBegin0.Addr(), 2, uintptr(engineHandle), uintptr(flags), 0)
+ if r1 != 0 {
+ if e1 != 0 {
+ err = errnoErr(e1)
+ } else {
+ err = syscall.EINVAL
+ }
+ }
+ return
+}
+
+func fwpmTransactionCommit0(engineHandle uintptr) (err error) {
+ r1, _, e1 := syscall.Syscall(procFwpmTransactionCommit0.Addr(), 1, uintptr(engineHandle), 0, 0)
+ if r1 != 0 {
+ if e1 != 0 {
+ err = errnoErr(e1)
+ } else {
+ err = syscall.EINVAL
+ }
+ }
+ return
+}
+
+func fwpmTransactionAbort0(engineHandle uintptr) (err error) {
+ r1, _, e1 := syscall.Syscall(procFwpmTransactionAbort0.Addr(), 1, uintptr(engineHandle), 0, 0)
+ if r1 != 0 {
+ if e1 != 0 {
+ err = errnoErr(e1)
+ } else {
+ err = syscall.EINVAL
+ }
+ }
+ return
+}
+
+func fwpmProviderAdd0(engineHandle uintptr, provider *wtFwpmProvider0, sd uintptr) (err error) {
+ r1, _, e1 := syscall.Syscall(procFwpmProviderAdd0.Addr(), 3, uintptr(engineHandle), uintptr(unsafe.Pointer(provider)), uintptr(sd))
+ if r1 != 0 {
+ if e1 != 0 {
+ err = errnoErr(e1)
+ } else {
+ err = syscall.EINVAL
+ }
+ }
+ return
+}
+
+func getSecurityInfo(handle windows.Handle, objectType wtObjectType, si uint32, sidOwner *windows.SID, sidGroup *windows.SID, dacl *uintptr, sacl *uintptr, securityDescriptor *uintptr) (err error) {
+ r1, _, e1 := syscall.Syscall9(procGetSecurityInfo.Addr(), 8, uintptr(handle), uintptr(objectType), uintptr(si), uintptr(unsafe.Pointer(sidOwner)), uintptr(unsafe.Pointer(sidGroup)), uintptr(unsafe.Pointer(dacl)), uintptr(unsafe.Pointer(sacl)), uintptr(unsafe.Pointer(securityDescriptor)), 0)
+ if r1 != 0 {
+ if e1 != 0 {
+ err = errnoErr(e1)
+ } else {
+ err = syscall.EINVAL
+ }
+ }
+ return
+}
+
+func getSecurityDescriptorLength(securityDescriptor uintptr) (len uint32) {
+ r0, _, _ := syscall.Syscall(procGetSecurityDescriptorLength.Addr(), 1, uintptr(securityDescriptor), 0, 0)
+ len = uint32(r0)
+ return
+}
diff --git a/tunnel/ifaceconfig.go b/tunnel/ifaceconfig.go
new file mode 100644
index 00000000..453d4ca5
--- /dev/null
+++ b/tunnel/ifaceconfig.go
@@ -0,0 +1,220 @@
+/* SPDX-License-Identifier: MIT
+ *
+ * Copyright (C) 2019 WireGuard LLC. All Rights Reserved.
+ */
+
+package tunnel
+
+import (
+ "bytes"
+ "log"
+ "net"
+ "sort"
+
+ "golang.org/x/sys/windows"
+ "golang.zx2c4.com/winipcfg"
+ "golang.zx2c4.com/wireguard/tun"
+
+ "golang.zx2c4.com/wireguard/windows/conf"
+ "golang.zx2c4.com/wireguard/windows/tunnel/firewall"
+)
+
+func cleanupAddressesOnDisconnectedInterfaces(addresses []*net.IPNet) {
+ if len(addresses) == 0 {
+ return
+ }
+ includedInAddresses := func(a *net.IPNet) bool {
+ //TODO: this makes the whole algorithm O(n^2). But we can't stick net.IPNet in a Go hashmap. Bummer!
+ for _, addr := range addresses {
+ ip := addr.IP
+ if ip4 := ip.To4(); ip4 != nil {
+ ip = ip4
+ }
+ mA, _ := addr.Mask.Size()
+ mB, _ := a.Mask.Size()
+ if bytes.Equal(ip, a.IP) && mA == mB {
+ return true
+ }
+ }
+ return false
+ }
+ interfaces, err := winipcfg.GetInterfaces()
+ if err != nil {
+ return
+ }
+ for _, iface := range interfaces {
+ if iface.OperStatus == winipcfg.IfOperStatusUp {
+ continue
+ }
+ addressesToKeep := make([]*net.IPNet, 0, len(iface.UnicastAddresses))
+ for _, address := range iface.UnicastAddresses {
+ ip := address.Address.Address
+ if ip4 := ip.To4(); ip4 != nil {
+ ip = ip4
+ }
+ ipnet := &net.IPNet{ip, net.CIDRMask(int(address.OnLinkPrefixLength), 8*len(ip))}
+ if !includedInAddresses(ipnet) {
+ addressesToKeep = append(addressesToKeep, ipnet)
+ }
+ }
+ if len(addressesToKeep) < len(iface.UnicastAddresses) {
+ log.Printf("Cleaning up stale addresses from interface '%s'", iface.FriendlyName)
+ iface.SetAddresses(addressesToKeep)
+ }
+ }
+}
+
+func configureInterface(conf *conf.Config, tun *tun.NativeTun) error {
+ iface, err := winipcfg.InterfaceFromLUID(tun.LUID())
+ if err != nil {
+ return err
+ }
+
+ estimatedRouteCount := len(conf.Interface.Addresses)
+ for _, peer := range conf.Peers {
+ estimatedRouteCount += len(peer.AllowedIPs)
+ }
+ routes := make([]winipcfg.RouteData, 0, estimatedRouteCount)
+ var firstGateway4 *net.IP
+ var firstGateway6 *net.IP
+ addresses := make([]*net.IPNet, len(conf.Interface.Addresses))
+ for i, addr := range conf.Interface.Addresses {
+ ipnet := addr.IPNet()
+ addresses[i] = &ipnet
+ gateway := ipnet.IP.Mask(ipnet.Mask)
+ if addr.Bits() == 32 && firstGateway4 == nil {
+ firstGateway4 = &gateway
+ } else if addr.Bits() == 128 && firstGateway6 == nil {
+ firstGateway6 = &gateway
+ }
+ routes = append(routes, winipcfg.RouteData{
+ Destination: net.IPNet{
+ IP: gateway,
+ Mask: ipnet.Mask,
+ },
+ NextHop: gateway,
+ Metric: 0,
+ })
+ }
+
+ foundDefault4 := false
+ foundDefault6 := false
+ for _, peer := range conf.Peers {
+ for _, allowedip := range peer.AllowedIPs {
+ if (allowedip.Bits() == 32 && firstGateway4 == nil) || (allowedip.Bits() == 128 && firstGateway6 == nil) {
+ continue
+ }
+ route := winipcfg.RouteData{
+ Destination: allowedip.IPNet(),
+ Metric: 0,
+ }
+ if allowedip.Bits() == 32 {
+ if allowedip.Cidr == 0 {
+ foundDefault4 = true
+ }
+ route.NextHop = *firstGateway4
+ } else if allowedip.Bits() == 128 {
+ if allowedip.Cidr == 0 {
+ foundDefault6 = true
+ }
+ route.NextHop = *firstGateway6
+ }
+ routes = append(routes, route)
+ }
+ }
+
+ err = iface.SetAddresses(addresses)
+ if err == windows.ERROR_OBJECT_ALREADY_EXISTS {
+ cleanupAddressesOnDisconnectedInterfaces(addresses)
+ err = iface.SetAddresses(addresses)
+ }
+ if err != nil {
+ return err
+ }
+
+ deduplicatedRoutes := make([]*winipcfg.RouteData, 0, len(routes))
+ sort.Slice(routes, func(i, j int) bool {
+ return routes[i].Metric < routes[j].Metric ||
+ bytes.Compare(routes[i].NextHop, routes[j].NextHop) == -1 ||
+ bytes.Compare(routes[i].Destination.IP, routes[j].Destination.IP) == -1 ||
+ bytes.Compare(routes[i].Destination.Mask, routes[j].Destination.Mask) == -1
+ })
+ for i := 0; i < len(routes); i++ {
+ if i > 0 && routes[i].Metric == routes[i-1].Metric &&
+ bytes.Equal(routes[i].NextHop, routes[i-1].NextHop) &&
+ bytes.Equal(routes[i].Destination.IP, routes[i-1].Destination.IP) &&
+ bytes.Equal(routes[i].Destination.Mask, routes[i-1].Destination.Mask) {
+ continue
+ }
+ deduplicatedRoutes = append(deduplicatedRoutes, &routes[i])
+ }
+
+ err = iface.SetRoutes(deduplicatedRoutes)
+ if err != nil {
+ return nil
+ }
+
+ err = iface.SetDNS(conf.Interface.DNS)
+ if err != nil {
+ return err
+ }
+
+ ipif, err := iface.GetIPInterface(windows.AF_INET)
+ if err != nil {
+ return err
+ }
+ if foundDefault4 {
+ ipif.UseAutomaticMetric = false
+ ipif.Metric = 0
+ }
+ if conf.Interface.MTU > 0 {
+ ipif.NLMTU = uint32(conf.Interface.MTU)
+ tun.ForceMTU(int(ipif.NLMTU))
+ }
+ err = ipif.Set()
+ if err != nil {
+ return err
+ }
+
+ ipif, err = iface.GetIPInterface(windows.AF_INET6)
+ if err != nil {
+ return err
+ }
+ if foundDefault6 {
+ ipif.UseAutomaticMetric = false
+ ipif.Metric = 0
+ }
+ if conf.Interface.MTU > 0 {
+ ipif.NLMTU = uint32(conf.Interface.MTU)
+ }
+ ipif.DadTransmits = 0
+ ipif.RouterDiscoveryBehavior = winipcfg.RouterDiscoveryDisabled
+ err = ipif.Set()
+ if err != nil {
+ return err
+ }
+
+ return nil
+}
+
+func enableFirewall(conf *conf.Config, tun *tun.NativeTun) error {
+ restrictAll := false
+ if len(conf.Peers) == 1 {
+ nextallowedip:
+ for _, allowedip := range conf.Peers[0].AllowedIPs {
+ if allowedip.Cidr == 0 {
+ for _, b := range allowedip.IP {
+ if b != 0 {
+ continue nextallowedip
+ }
+ }
+ restrictAll = true
+ break
+ }
+ }
+ }
+ if restrictAll && len(conf.Interface.DNS) == 0 {
+ log.Println("Warning: no DNS server specified, despite having an allowed IPs of 0.0.0.0/0 or ::/0. There may be connectivity issues.")
+ }
+ return firewall.EnableFirewall(tun.LUID(), conf.Interface.DNS, restrictAll)
+}
diff --git a/tunnel/service.go b/tunnel/service.go
new file mode 100644
index 00000000..e93a2c40
--- /dev/null
+++ b/tunnel/service.go
@@ -0,0 +1,225 @@
+/* SPDX-License-Identifier: MIT
+ *
+ * Copyright (C) 2017-2019 WireGuard LLC. All Rights Reserved.
+ */
+
+package tunnel
+
+import (
+ "bufio"
+ "bytes"
+ "fmt"
+ "log"
+ "net"
+ "os"
+ "runtime"
+ "runtime/debug"
+ "strings"
+ "time"
+
+ "golang.org/x/sys/windows/svc"
+ "golang.zx2c4.com/winipcfg"
+ "golang.zx2c4.com/wireguard/device"
+ "golang.zx2c4.com/wireguard/ipc"
+ "golang.zx2c4.com/wireguard/tun"
+
+ "golang.zx2c4.com/wireguard/windows/conf"
+ "golang.zx2c4.com/wireguard/windows/ringlogger"
+ "golang.zx2c4.com/wireguard/windows/services"
+ "golang.zx2c4.com/wireguard/windows/version"
+)
+
+type Service struct {
+ Path string
+}
+
+func (service *Service) Execute(args []string, r <-chan svc.ChangeRequest, changes chan<- svc.Status) (svcSpecificEC bool, exitCode uint32) {
+ changes <- svc.Status{State: svc.StartPending}
+
+ var dev *device.Device
+ var uapi net.Listener
+ var routeChangeCallback *winipcfg.RouteChangeCallback
+ var err error
+ serviceError := services.ErrorSuccess
+
+ defer func() {
+ svcSpecificEC, exitCode = services.DetermineErrorCode(err, serviceError)
+ logErr := services.CombineErrors(err, serviceError)
+ if logErr != nil {
+ log.Println(logErr)
+ }
+ changes <- svc.Status{State: svc.StopPending}
+
+ stopIt := make(chan bool, 1)
+ go func() {
+ t := time.NewTicker(time.Second * 30)
+ for {
+ select {
+ case <-t.C:
+ t.Stop()
+ buf := make([]byte, 1024)
+ for {
+ n := runtime.Stack(buf, true)
+ if n < len(buf) {
+ buf = buf[:n]
+ break
+ }
+ buf = make([]byte, 2*len(buf))
+ }
+ lines := bytes.Split(buf, []byte{'\n'})
+ log.Println("Failed to shutdown after 30 seconds. Probably dead locked. Printing stack and killing.")
+ for _, line := range lines {
+ if len(bytes.TrimSpace(line)) > 0 {
+ log.Println(string(line))
+ }
+ }
+ os.Exit(777)
+ return
+ case <-stopIt:
+ t.Stop()
+ return
+ }
+ }
+ }()
+
+ if routeChangeCallback != nil {
+ routeChangeCallback.Unregister()
+ }
+ if uapi != nil {
+ uapi.Close()
+ }
+ if dev != nil {
+ dev.Close()
+ }
+ stopIt <- true
+ log.Println("Shutting down")
+ }()
+
+ err = ringlogger.InitGlobalLogger("TUN")
+ if err != nil {
+ serviceError = services.ErrorRingloggerOpen
+ return
+ }
+ defer func() {
+ if x := recover(); x != nil {
+ for _, line := range append([]string{fmt.Sprint(x)}, strings.Split(string(debug.Stack()), "\n")...) {
+ if len(strings.TrimSpace(line)) > 0 {
+ log.Println(line)
+ }
+ }
+ panic(x)
+ }
+ }()
+
+ conf, err := conf.LoadFromPath(service.Path)
+ if err != nil {
+ serviceError = services.ErrorLoadConfiguration
+ return
+ }
+
+ logPrefix := fmt.Sprintf("[%s] ", conf.Name)
+ log.SetPrefix(logPrefix)
+
+ log.Println("Starting", version.UserAgent())
+
+ log.Println("Resolving DNS names")
+ uapiConf, err := conf.ToUAPI()
+ if err != nil {
+ serviceError = services.ErrorDNSLookup
+ return
+ }
+
+ log.Println("Creating Wintun device")
+ wintun, err := tun.CreateTUN(conf.Name)
+ if err != nil {
+ serviceError = services.ErrorCreateWintun
+ return
+ }
+ log.Println("Determining Wintun device name")
+ realInterfaceName, err := wintun.Name()
+ if err != nil {
+ serviceError = services.ErrorDetermineWintunName
+ return
+ }
+ conf.Name = realInterfaceName
+ nativeTun := wintun.(*tun.NativeTun)
+
+ log.Println("Enabling firewall rules")
+ err = enableFirewall(conf, nativeTun)
+ if err != nil {
+ serviceError = services.ErrorFirewall
+ return
+ }
+
+ log.Println("Dropping all privileges")
+ err = services.DropAllPrivileges()
+ if err != nil {
+ serviceError = services.ErrorDropPrivileges
+ return
+ }
+
+ log.Println("Creating interface instance")
+ logOutput := log.New(ringlogger.Global, logPrefix, 0)
+ logger := &device.Logger{logOutput, logOutput, logOutput}
+ dev = device.NewDevice(wintun, logger)
+
+ log.Println("Setting interface configuration")
+ uapi, err = ipc.UAPIListen(conf.Name)
+ if err != nil {
+ serviceError = services.ErrorUAPIListen
+ return
+ }
+ ipcErr := dev.IpcSetOperation(bufio.NewReader(strings.NewReader(uapiConf)))
+ if ipcErr != nil {
+ err = ipcErr
+ serviceError = services.ErrorDeviceSetConfig
+ return
+ }
+
+ log.Println("Bringing peers up")
+ dev.Up()
+
+ log.Println("Monitoring default routes")
+ routeChangeCallback, err = monitorDefaultRoutes(dev, conf.Interface.MTU == 0, nativeTun)
+ if err != nil {
+ serviceError = services.ErrorBindSocketsToDefaultRoutes
+ return
+ }
+
+ log.Println("Setting device address")
+ err = configureInterface(conf, nativeTun)
+ if err != nil {
+ serviceError = services.ErrorSetNetConfig
+ return
+ }
+
+ log.Println("Listening for UAPI requests")
+ go func() {
+ for {
+ conn, err := uapi.Accept()
+ if err != nil {
+ continue
+ }
+ go dev.IpcHandle(conn)
+ }
+ }()
+
+ changes <- svc.Status{State: svc.Running, Accepts: svc.AcceptStop}
+ log.Println("Startup complete")
+
+ for {
+ select {
+ case c := <-r:
+ switch c.Cmd {
+ case svc.Stop:
+ return
+ case svc.Interrogate:
+ changes <- c.CurrentStatus
+ default:
+ log.Printf("Unexpected service control request #%d\n", c)
+ }
+ case <-dev.Wait():
+ return
+ }
+ }
+}