diff --git a/.github/workflows/kext.yml b/.github/workflows/kext.yml
new file mode 100644
index 000000000..6ccdee66c
--- /dev/null
+++ b/.github/workflows/kext.yml
@@ -0,0 +1,36 @@
+name: Windows Kernel Extension
+
+on:
+ push:
+ paths:
+ - 'windows_kext/**'
+ branches:
+ - master
+ - develop
+
+ pull_request:
+ paths:
+ - 'windows_kext/**'
+ branches:
+ - master
+ - develop
+
+jobs:
+ build:
+ name: Build
+ runs-on: ubuntu-latest
+ steps:
+ - uses: earthly/actions-setup@v1
+ with:
+ version: v0.8.0
+ - uses: actions/checkout@v4
+
+ - name: Log in to the Container registry
+ uses: docker/login-action@65b78e6e13532edd9afa3aa52ac7964289d1a9c1
+ with:
+ registry: ghcr.io
+ username: ${{ github.actor }}
+ password: ${{ secrets.GITHUB_TOKEN }}
+
+ - name: Build Kernel Extension
+ run: earthly --ci --remote-cache=ghcr.io/safing/build-cache --push +kext-build
diff --git a/.gitignore b/.gitignore
index 8268448fb..d534dccd8 100644
--- a/.gitignore
+++ b/.gitignore
@@ -48,3 +48,6 @@ _testmain.go
win_dev_*
go.work
go.work.sum
+
+# Kext releases
+windows_kext/release/kext_release_*.zip
diff --git a/Earthfile b/Earthfile
index 5d9d3394d..d691fc37d 100644
--- a/Earthfile
+++ b/Earthfile
@@ -3,6 +3,7 @@ VERSION --arg-scope-and-set --global-cache 0.8
ARG --global go_version = 1.22
ARG --global node_version = 18
ARG --global rust_version = 1.76
+ARG --global golangci_lint_version = 1.57.1
ARG --global go_builder_image = "golang:${go_version}-alpine"
ARG --global node_builder_image = "node:${node_version}"
@@ -164,6 +165,12 @@ go-test-all:
BUILD +go-test --GOARCH="${GOARCH}" --GOOS="${GOOS}" --GOARM="${GOARM}"
END
+go-lint:
+ FROM +go-base
+
+ RUN go install github.com/golangci/golangci-lint/cmd/golangci-lint@v${golangci_lint_version}
+ RUN golangci-lint run -c ./.golangci.yml --timeout 15m --show-stats
+
# Builds portmaster-start, portmaster-core, hub and notifier for all supported platforms
go-release:
FROM ${work_image}
@@ -454,6 +461,27 @@ tauri-release:
BUILD +tauri-build --target="${arch}" --bundle="${bundle}"
END
+kext-build:
+ FROM ${rust_builder_image}
+
+ # Install architecture target
+ DO rust+INIT --keep_fingerprints=true
+
+ # Build kext
+ WORKDIR /app/kext
+ # --keep-ts is necessary to ensure that the timestamps of the source files
+ # are preserved such that Rust's incremental compilation works correctly.
+ COPY --keep-ts ./windows_kext/ .
+
+ # Add target architecture
+ RUN rustup target add x86_64-pc-windows-msvc
+
+ # Build using special earthly lib
+ WORKDIR /app/kext/release
+ DO rust+CARGO --args="run"
+
+ SAVE ARTIFACT --keep-ts "portmaster-kext-release-bundle.zip" AS LOCAL "${outputDir}/windows_amd64/portmaster-kext-release-bundle.zip"
+
build:
BUILD +go-release
BUILD +angular-release
@@ -466,7 +494,7 @@ release:
RUN echo -e "\033[1;31m Refusing to release a dirty git repository. Please commit your local changes first! \033[0m" ; exit 1
END
- BUILD +build-all
+ BUILD +build
# Takes GOOS, GOARCH and optionally GOARM and creates a string representation for file-names.
diff --git a/go.sum b/go.sum
index 5f2b38340..b008d9881 100644
--- a/go.sum
+++ b/go.sum
@@ -5,8 +5,6 @@ github.com/AndreasBriese/bbloom v0.0.0-20190825152654-46b345b51c96 h1:cTp8I5+VIo
github.com/AndreasBriese/bbloom v0.0.0-20190825152654-46b345b51c96/go.mod h1:bOvUY6CB00SOBii9/FifXqc0awNKxLFCL/+pkDPuyl8=
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
github.com/BurntSushi/toml v0.4.1/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ=
-github.com/VictoriaMetrics/metrics v1.29.1 h1:yTORfGeO1T0C6P/tEeT4Mf7rBU5TUu3kjmHvmlaoeO8=
-github.com/VictoriaMetrics/metrics v1.29.1/go.mod h1:r7hveu6xMdUACXvB8TYdAj8WEsKzWB0EkpJN+RDtOf8=
github.com/VictoriaMetrics/metrics v1.33.1 h1:CNV3tfm2Kpv7Y9W3ohmvqgFWPR55tV2c7M2U6OIo+UM=
github.com/VictoriaMetrics/metrics v1.33.1/go.mod h1:r7hveu6xMdUACXvB8TYdAj8WEsKzWB0EkpJN+RDtOf8=
github.com/Xuanwo/go-locale v1.1.0 h1:51gUxhxl66oXAjI9uPGb2O0qwPECpriKQb2hl35mQkg=
@@ -32,8 +30,6 @@ github.com/cespare/xxhash/v2 v2.2.0 h1:DC2CZ1Ep5Y4k3ZQ899DldepgrayRUGE6BBZ/cd9Cj
github.com/cespare/xxhash/v2 v2.2.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
github.com/cilium/ebpf v0.5.0/go.mod h1:4tRaxcgiL706VnOzHOdBlY8IEAIdxINsQBcU4xJJXRs=
github.com/cilium/ebpf v0.7.0/go.mod h1:/oI2+1shJiTGAMgl6/RgJr36Eo1jzrRcAWbcXO2usCA=
-github.com/cilium/ebpf v0.12.3 h1:8ht6F9MquybnY97at+VDZb3eQQr8ev79RueWeVaEcG4=
-github.com/cilium/ebpf v0.12.3/go.mod h1:TctK1ivibvI3znr66ljgi4hqOT8EYQjz1KWBfb1UVgM=
github.com/cilium/ebpf v0.14.0 h1:0PsxAjO6EjI1rcT+rkp6WcCnE0ZvfkXBYiMedJtrSUs=
github.com/cilium/ebpf v0.14.0/go.mod h1:DHp1WyrLeiBh19Cf/tfiSMhqheEiK8fXFZ4No0P1Hso=
github.com/cilium/ebpf v0.15.0 h1:7NxJhNiBT3NG8pZJ3c+yfrVdHY8ScgKD27sScgjLMMk=
@@ -65,15 +61,10 @@ github.com/florianl/go-nfqueue v1.3.1 h1:khQ9fYCrjbu5CF8dZF55G2RTIEIQRI0Aj5k3msJ
github.com/florianl/go-nfqueue v1.3.1/go.mod h1:aHWbgkhryJxF5XxYvJ3oRZpdD4JP74Zu/hP1zuhja+M=
github.com/fogleman/gg v1.3.0 h1:/7zJX8F6AaYQc57WQCyN9cAIz+4bCJGO9B+dyW29am8=
github.com/fogleman/gg v1.3.0/go.mod h1:R/bRT+9gY/C5z7JzPU0zXsXHKM4/ayA+zqcVNZzPa1k=
-github.com/frankban/quicktest v1.11.3 h1:8sXhOn0uLys67V8EsXLc6eszDs8VXWxL3iRvebPhedY=
github.com/frankban/quicktest v1.11.3/go.mod h1:wRf/ReqHper53s+kmmSZizM8NamnL3IM0I9ntUbOk+k=
-github.com/frankban/quicktest v1.14.5 h1:dfYrrRyLtiqT9GyKXgdh+k4inNeTvmGbuSgZ3lx3GhA=
-github.com/frankban/quicktest v1.14.5/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7zb5vbUoiM6w0=
github.com/fsnotify/fsnotify v1.4.3-0.20170329110642-4da3e2cfbabc/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
github.com/fxamacker/cbor v1.5.1 h1:XjQWBgdmQyqimslUh5r4tUGmoqzHmBFQOImkWGi2awg=
github.com/fxamacker/cbor v1.5.1/go.mod h1:3aPGItF174ni7dDzd6JZ206H8cmr4GDNBGpPa971zsU=
-github.com/fxamacker/cbor/v2 v2.5.0 h1:oHsG0V/Q6E/wqTS2O1Cozzsy69nqCiguo5Q1a1ADivE=
-github.com/fxamacker/cbor/v2 v2.5.0/go.mod h1:TA1xS00nchWmaBnEIxPSE5oHLuJBAVvqrtAnWBwBCVo=
github.com/fxamacker/cbor/v2 v2.6.0 h1:sU6J2usfADwWlYDAFhZBQ6TnLFBHxgesMrQfQgk1tWA=
github.com/fxamacker/cbor/v2 v2.6.0/go.mod h1:pxXPTn3joSm21Gbwsv0w9OSA2y1HFR9qXEeXQVeNoDQ=
github.com/garyburd/redigo v1.1.1-0.20170914051019-70e1b1943d4f/go.mod h1:NR3MbYisc3/PwhQ00EMzDiPmrwpPxAn5GI05/YaO1SY=
@@ -82,6 +73,8 @@ github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeME
github.com/go-ole/go-ole v1.2.6/go.mod h1:pprOEPIfldk/42T2oK7lQ4v4JSDwmV0As9GaiUsvbm0=
github.com/go-ole/go-ole v1.3.0 h1:Dt6ye7+vXGIKZ7Xtk4s6/xVdGDQynvom7xCFEdWr6uE=
github.com/go-ole/go-ole v1.3.0/go.mod h1:5LS6F96DhAwUc7C+1HLexzMXY1xGRSryjyPPKW6zv78=
+github.com/go-quicktest/qt v1.101.0 h1:O1K29Txy5P2OK0dGo59b7b0LR6wKfIhttaAhHUyn7eI=
+github.com/go-quicktest/qt v1.101.0/go.mod h1:14Bz/f7NwaXPtdYEgzsx46kqSxVwTbzVZsDC26tQJow=
github.com/go-stack/stack v1.6.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
github.com/godbus/dbus v4.1.0+incompatible h1:WqqLRTsQic3apZUK9qC5sGNfXthmPXzUZ7nQPrNITa4=
github.com/godbus/dbus v4.1.0+incompatible/go.mod h1:/YcGZj5zSblfDWMMoOzV4fas9FZnQYTkDnsGvmh2Grw=
@@ -120,8 +113,6 @@ github.com/google/gopacket v1.1.19 h1:ves8RnFZPGiFnTS0uPQStjwru6uO6h+nlr9j6fL7kF
github.com/google/gopacket v1.1.19/go.mod h1:iJ8V8n6KS+z2U1A8pUwu8bW5SyEMkXJB8Yo/Vo+TKTo=
github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 h1:El6M4kTTCOh6aBiKaUGG7oYTSPP8MxqL4YI3kZKwcP4=
github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510/go.mod h1:pupxD2MaaD3pAXIBCelhxNneeOaAeabZDe5s4K6zSpQ=
-github.com/google/uuid v1.5.0 h1:1p67kYwdtXjb0gL0BPiP1Av9wiZPo5A8z2cWkTZ+eyU=
-github.com/google/uuid v1.5.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/googleapis/gax-go v2.0.0+incompatible/go.mod h1:SFVmujtThgffbyetf+mdk2eWhX2bMyUtNHzFKcPA9HY=
@@ -161,8 +152,6 @@ github.com/jsimonetti/rtnetlink v0.0.0-20211022192332-93da33804786/go.mod h1:v4h
github.com/jtolds/gls v4.20.0+incompatible h1:xdiiI2gbIgH/gLH7ADydsJ1uDOEzR8yvV7C0MuV77Wo=
github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU=
github.com/klauspost/cpuid/v2 v2.0.12/go.mod h1:g2LTdtYhdyuGPqyWyv7qRAmj1WBqxuObKfj5c0PQa7c=
-github.com/klauspost/cpuid/v2 v2.2.6 h1:ndNyv040zDGIDh8thGkXYjnFtiN02M1PVVF+JE/48xc=
-github.com/klauspost/cpuid/v2 v2.2.6/go.mod h1:Lcz8mBdAVJIBVzewtcLocK12l3Y+JytZYpaMropDUws=
github.com/klauspost/cpuid/v2 v2.2.7 h1:ZWSB3igEs+d0qvnxR/ZBzXVmxkgt8DdzP6m9pfuVLDM=
github.com/klauspost/cpuid/v2 v2.2.7/go.mod h1:Lcz8mBdAVJIBVzewtcLocK12l3Y+JytZYpaMropDUws=
github.com/kr/pretty v0.2.0/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
@@ -201,12 +190,8 @@ github.com/mdlayher/socket v0.0.0-20210307095302-262dc9984e00/go.mod h1:GAFlyu4/
github.com/mdlayher/socket v0.0.0-20211007213009-516dcbdf0267/go.mod h1:nFZ1EtZYK8Gi/k6QNu7z7CgO20i/4ExeQswwWuPmG/g=
github.com/mdlayher/socket v0.1.0/go.mod h1:mYV5YIZAfHh4dzDVzI8x8tWLWCliuX8Mon5Awbj+qDs=
github.com/mdlayher/socket v0.1.1/go.mod h1:mYV5YIZAfHh4dzDVzI8x8tWLWCliuX8Mon5Awbj+qDs=
-github.com/mdlayher/socket v0.5.0 h1:ilICZmJcQz70vrWVes1MFera4jGiWNocSkykwwoy3XI=
-github.com/mdlayher/socket v0.5.0/go.mod h1:WkcBFfvyG8QENs5+hfQPl1X6Jpd2yeLIYgrGFmJiJxI=
github.com/mdlayher/socket v0.5.1 h1:VZaqt6RkGkt2OE9l3GcC6nZkqD3xKeQLyfleW/uBcos=
github.com/mdlayher/socket v0.5.1/go.mod h1:TjPLHI1UgwEv5J1B5q0zTZq12A/6H7nKmtTanQE37IQ=
-github.com/miekg/dns v1.1.57 h1:Jzi7ApEIzwEPLHWRcafCN9LZSBbqQpxjt/wpgvg7wcM=
-github.com/miekg/dns v1.1.57/go.mod h1:uqRjCRUuEAA6qsOiJvDd+CFo/vW+y5WR6SNmHE55hZk=
github.com/miekg/dns v1.1.58 h1:ca2Hdkz+cDg/7eNF6V56jjzuZ4aCAE+DbVkILdQWG/4=
github.com/miekg/dns v1.1.58/go.mod h1:Ypv+3b/KadlvW9vJfXOTf300O4UqaHFzFCuHz+rPkBY=
github.com/miekg/dns v1.1.59 h1:C9EXc/UToRwKLhK5wKU/I4QVsBUc8kE6MkHBkeypWZs=
@@ -235,32 +220,19 @@ github.com/r3labs/diff/v3 v3.0.1 h1:CBKqf3XmNRHXKmdU7mZP1w7TV0pDyVCis1AUHtA4Xtg=
github.com/r3labs/diff/v3 v3.0.1/go.mod h1:f1S9bourRbiM66NskseyUdo0fTmEE0qKrikYJX63dgo=
github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec h1:W09IVJc94icq4NjY3clb7Lk8O1qJ8BdBEF8z0ibU0rE=
github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo=
-github.com/rogpeppe/go-internal v1.9.0 h1:73kH8U+JUqXU8lRuOHeVHaa/SZPifC7BkcraZVejAe8=
-github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs=
github.com/rogpeppe/go-internal v1.11.0 h1:cWPaGQEPrBb5/AsnsZesgZZ9yb1OQ+GOISoDNXVBh4M=
+github.com/rogpeppe/go-internal v1.11.0/go.mod h1:ddIwULY96R17DhadqLgMfk9H9tvdUzkipdSkR5nkCZA=
github.com/rot256/pblind v0.0.0-20231024115251-cd3f239f28c1 h1:vfAp3Jbca7Vt8axzmkS5M/RtFJmj0CKmrtWAlHtesaA=
github.com/rot256/pblind v0.0.0-20231024115251-cd3f239f28c1/go.mod h1:2x8fbm9T+uTl919COhEVHKGkve1DnkrEnDbtGptZuW8=
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
github.com/safing/jess v0.3.3 h1:0U0bWdO0sFCgox+nMOqISFrnJpVmi+VFOW1xdX6q3qw=
github.com/safing/jess v0.3.3/go.mod h1:t63qHB+4xd1HIv9MKN/qI2rc7ytvx7d6l4hbX7zxer0=
-github.com/safing/portbase v0.18.9 h1:j+ToHKQz0U2+Tx4jMP7QPky/H0R4uY6qUM+lIJlO6ks=
-github.com/safing/portbase v0.18.9/go.mod h1:Qrh3ck+7VZloFmnozCs9Hj8godhJAi55cmiDiC7BwTc=
-github.com/safing/portbase v0.19.0 h1:2T6f/w90IdIsSgUfyXoveqZM7tVwW+IFrtLbPVXtY3k=
-github.com/safing/portbase v0.19.0/go.mod h1:Qrh3ck+7VZloFmnozCs9Hj8godhJAi55cmiDiC7BwTc=
-github.com/safing/portbase v0.19.1 h1:Uk/WyP9HsIJrCn0pE4a7AWIrfUSHyCOObQyRmXsGQ9A=
-github.com/safing/portbase v0.19.1/go.mod h1:Qrh3ck+7VZloFmnozCs9Hj8godhJAi55cmiDiC7BwTc=
-github.com/safing/portbase v0.19.2 h1:qGF5Jv9eEE33d2aIxeBQdnitnBoF44BGVFtboqfE+1A=
-github.com/safing/portbase v0.19.2/go.mod h1:Qrh3ck+7VZloFmnozCs9Hj8godhJAi55cmiDiC7BwTc=
-github.com/safing/portbase v0.19.3 h1:fzb4d2nzhmRq4Lt6sgn9R20iykireAkBNyf9pfGqQjk=
-github.com/safing/portbase v0.19.3/go.mod h1:Qrh3ck+7VZloFmnozCs9Hj8godhJAi55cmiDiC7BwTc=
github.com/safing/portbase v0.19.4 h1:Oh7oUBp6xn5whhKtvnNKS5rhHqyXJDDxfxwf+gRswhQ=
github.com/safing/portbase v0.19.4/go.mod h1:Qrh3ck+7VZloFmnozCs9Hj8godhJAi55cmiDiC7BwTc=
github.com/safing/portbase v0.19.5 h1:3/8odzlvb629tHPwdj/sthSeJcwZHYrqA6YuvNUZzNc=
github.com/safing/portbase v0.19.5/go.mod h1:Qrh3ck+7VZloFmnozCs9Hj8godhJAi55cmiDiC7BwTc=
github.com/safing/portmaster-android/go v0.0.0-20230830120134-3226ceac3bec h1:oSJY1seobofPwpMoJRkCgXnTwfiQWNfGMCPDfqgAEfg=
github.com/safing/portmaster-android/go v0.0.0-20230830120134-3226ceac3bec/go.mod h1:abwyAQrZGemWbSh/aCD9nnkp0SvFFf/mGWkAbOwPnFE=
-github.com/safing/spn v0.7.5 h1:WfkMs2omLrwxBWccGGG9Akx0AvsvJLG+W7rjWQpQhl4=
-github.com/safing/spn v0.7.5/go.mod h1:Hg585WJuib4JI3R7Kndq/10MJPCUl1UmeJJwL3JIwdQ=
github.com/satori/go.uuid v1.2.0 h1:0uYX9dsZ2yD7q2RtLRtPSdGDWzjeM3TbMJP9utgA0ww=
github.com/satori/go.uuid v1.2.0/go.mod h1:dA0hQrYB0VpLJoorglMZABFdXlWrHn1NEOzdhQKdks0=
github.com/seehuhn/fortuna v1.0.1 h1:lu9+CHsmR0bZnx5Ay646XvCSRJ8PJTi5UYJwDBX68H0=
@@ -298,8 +270,6 @@ github.com/tannerryan/ring v1.1.2/go.mod h1:DkELJEjbZhJBtFKR9Xziwj3HKZnb/knRgljN
github.com/tevino/abool v1.2.0 h1:heAkClL8H6w+mK5md9dzsuohKeXHUpY7Vw0ZCKW+huA=
github.com/tevino/abool v1.2.0/go.mod h1:qc66Pna1RiIsPa7O4Egxxs9OqkuxDX55zznh9K07Tzg=
github.com/tidwall/gjson v1.14.2/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk=
-github.com/tidwall/gjson v1.17.0 h1:/Jocvlh98kcTfpN2+JzGQWQcqrPQwDrVEMApx/M5ZwM=
-github.com/tidwall/gjson v1.17.0/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk=
github.com/tidwall/gjson v1.17.1 h1:wlYEnwqAHgzmhNUFfw7Xalt2JzQvsMx2Se4PcoFCT/U=
github.com/tidwall/gjson v1.17.1/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk=
github.com/tidwall/match v1.1.1 h1:+Ho715JplO36QYgwN9PGYNhgZvoUSc9X2c80KVTi+GA=
@@ -330,8 +300,6 @@ github.com/x448/float16 v0.8.4 h1:qLwI1I70+NjRFUR3zs1JPUCgaCXSh3SW62uAKT1mSBM=
github.com/x448/float16 v0.8.4/go.mod h1:14CWIYCyZA/cWjXOioeEpHeN/83MdbZDRQHoFcYsOfg=
github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
github.com/yuin/goldmark v1.4.0/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
-github.com/yusufpapurcu/wmi v1.2.3 h1:E1ctvB7uKFMOJw3fdOW32DwGE9I7t++CRUEMKvFoFiw=
-github.com/yusufpapurcu/wmi v1.2.3/go.mod h1:SBZ9tNy3G9/m5Oi98Zks0QjeHVDvuK0qfxQmPyzfmi0=
github.com/yusufpapurcu/wmi v1.2.4 h1:zFUKzehAFReQwLys1b/iSMl+JQGSCSjtVqQn9bBrPo0=
github.com/yusufpapurcu/wmi v1.2.4/go.mod h1:SBZ9tNy3G9/m5Oi98Zks0QjeHVDvuK0qfxQmPyzfmi0=
github.com/zalando/go-keyring v0.2.3 h1:v9CUu9phlABObO4LPWycf+zwMG7nlbb3t/B5wa97yms=
@@ -344,22 +312,14 @@ github.com/zeebo/blake3 v0.2.3 h1:TFoLXsjeXqRNFxSbk35Dk4YtszE/MQQGK10BH4ptoTg=
github.com/zeebo/blake3 v0.2.3/go.mod h1:mjJjZpnsyIVtVgTOSpJ9vmRE4wgDeyt2HU3qXvvKCaQ=
github.com/zeebo/pcg v1.0.1 h1:lyqfGeWiv4ahac6ttHs+I5hwtH/+1mrhlCtVNQM2kHo=
github.com/zeebo/pcg v1.0.1/go.mod h1:09F0S9iiKrwn9rlI5yjLkmrug154/YRW6KnnXVDM/l4=
-go.etcd.io/bbolt v1.3.8 h1:xs88BrvEv273UsB79e0hcVrlUWmS0a8upikMFhSyAtA=
-go.etcd.io/bbolt v1.3.8/go.mod h1:N9Mkw9X8x5fupy0IKsmuqVtoGDyxsaDlbk4Rd05IAQw=
go.etcd.io/bbolt v1.3.9 h1:8x7aARPEXiXbHmtUwAIv7eV2fQFHrLLavdiJ3uzJXoI=
go.etcd.io/bbolt v1.3.9/go.mod h1:zaO32+Ti0PK1ivdPtgMESzuzL2VPoIG1PCQNvOdo/dE=
golang.org/x/crypto v0.0.0-20190211182817-74369b46fc67/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
-golang.org/x/crypto v0.18.0 h1:PGVlW0xEltQnzFZ55hkuX5+KLyrMYhHld1YHO4AKcdc=
-golang.org/x/crypto v0.18.0/go.mod h1:R0j02AL6hcrfOiy9T4ZYp/rcWeMxM3L6QYxlOuEG1mg=
-golang.org/x/crypto v0.21.0 h1:X31++rzVUdKhX5sWmSOFZxx8UW/ldWx55cbf08iNAMA=
-golang.org/x/crypto v0.21.0/go.mod h1:0BP7YvVV9gBbVKyeTG0Gyn+gZm94bibOW5BjDEYAOMs=
golang.org/x/crypto v0.22.0 h1:g1v0xeRhjcugydODzvb3mEM9SQ0HGp9s/nh3COQ/C30=
golang.org/x/crypto v0.22.0/go.mod h1:vr6Su+7cTlO45qkww3VDJlzDn0ctJvRgYbC2NvXHt+M=
-golang.org/x/exp v0.0.0-20240110193028-0dcbfd608b1e h1:723BNChdd0c2Wk6WOE320qGBiPtYx0F0Bbm1kriShfE=
-golang.org/x/exp v0.0.0-20240110193028-0dcbfd608b1e/go.mod h1:iRJReGqOEeBhDZGkGbynYwcHlctCvnjTYIamk7uXpHI=
golang.org/x/exp v0.0.0-20240409090435-93d18d7e34b8 h1:ESSUROHIBHg7USnszlcdmjBEwdMj9VUvU+OPk4yl2mc=
golang.org/x/exp v0.0.0-20240409090435-93d18d7e34b8/go.mod h1:/lliqkxwWAhPjf5oSOIJup2XcqJaw8RGS6k3TGEc7GI=
golang.org/x/exp v0.0.0-20240416160154-fe59bbe5cc7f h1:99ci1mjWVBWwJiEKYY6jWa4d2nTQVIEhZIptnrVb1XY=
@@ -371,8 +331,6 @@ golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzB
golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.5.1/go.mod h1:5OXOZSfqPIIbmVBIIKWRFfZjPR0E5r58TLhUjH0a2Ro=
-golang.org/x/mod v0.14.0 h1:dGoOF9QVLYng8IHTm7BAyWqCqSheQ5pYWGhzW00YJr0=
-golang.org/x/mod v0.14.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
golang.org/x/mod v0.17.0 h1:zY54UmvipHiNd+pm+m0x9KhZ9hl1/7QNMyxXbc6ICqA=
golang.org/x/mod v0.17.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
@@ -396,10 +354,6 @@ golang.org/x/net v0.0.0-20211020060615-d418f374d309/go.mod h1:9nx3DQGgdP8bBQD5qx
golang.org/x/net v0.0.0-20211209124913-491a49abca63/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/net v0.0.0-20220107192237-5cfca573fb4d/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/net v0.0.0-20220127200216-cd36cc0744dd/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk=
-golang.org/x/net v0.20.0 h1:aCL9BSgETF1k+blQaYUBx9hJ9LOGP3gAVemcZlf1Kpo=
-golang.org/x/net v0.20.0/go.mod h1:z8BVo6PvndSri0LbOE3hAn0apkU+1YvI6E70E9jsnvY=
-golang.org/x/net v0.22.0 h1:9sGLhx7iRIHEiX0oAJ3MRZMUCElJgy7Br1nO+AMN3Tc=
-golang.org/x/net v0.22.0/go.mod h1:JKghWKKOSdJwpW2GEx0Ja7fmaKnMsbu+MWVZTokSYmg=
golang.org/x/net v0.24.0 h1:1PcaxkF854Fu3+lvBIx5SYn9wRlBzzcnHZSiaFFAb0w=
golang.org/x/net v0.24.0/go.mod h1:2Q7sJY5mzlzWjKtYUEXSlBWCdyaioyXzRB2RtU8KVE8=
golang.org/x/oauth2 v0.0.0-20170912212905-13449ad91cb2/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
@@ -407,8 +361,6 @@ golang.org/x/sync v0.0.0-20170517211232-f52d1811a629/go.mod h1:RxMgew5VJxzue5/jJ
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
-golang.org/x/sync v0.6.0 h1:5BMeUDZ7vkXGfEr1x9B4bRcTH4lpkTkpdh0T/J+qjbQ=
-golang.org/x/sync v0.6.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
golang.org/x/sync v0.7.0 h1:YsImfSBoP9QPYL0xyKJPq0gcaJdG3rInoqxTWbfQu9M=
golang.org/x/sync v0.7.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
@@ -445,10 +397,6 @@ golang.org/x/sys v0.0.0-20220128215802-99c3d69c2c27/go.mod h1:oPkhp1MJrh7nUepCBc
golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
-golang.org/x/sys v0.16.0 h1:xWw16ngr6ZMtmxDyKyIgsE93KNKz5HKmMa3b8ALHidU=
-golang.org/x/sys v0.16.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
-golang.org/x/sys v0.18.0 h1:DBdB3niSjOA/O0blCZBqDefyWNYveAYMNF1Wum0DYQ4=
-golang.org/x/sys v0.18.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/sys v0.19.0 h1:q5f1RH2jigJ1MoAWp2KTp3gm5zAGFUTarQZ5U386+4o=
golang.org/x/sys v0.19.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
@@ -469,8 +417,6 @@ golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtn
golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0=
golang.org/x/tools v0.1.7/go.mod h1:LGqMHiF4EqQNHR1JncWGqT5BVaXmza+X+BDGol+dOxo=
-golang.org/x/tools v0.16.1 h1:TLyB3WofjdOEepBHAU20JdNC1Zbg87elYofWYAY5oZA=
-golang.org/x/tools v0.16.1/go.mod h1:kYVVN6I1mBNoB1OX+noeBjbRk4IUEPa7JJ+TJMEooJ0=
golang.org/x/tools v0.20.0 h1:hz/CVckiOxybQvFw6h7b/q80NTr9IUQb4s1IIzW7KNY=
golang.org/x/tools v0.20.0/go.mod h1:WvitBU7JJf6A4jOdg4S1tviW9bhUxkgeCui/0JHctQg=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
@@ -492,30 +438,32 @@ gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
-gvisor.dev/gvisor v0.0.0-20240110202538-8053cd8f0bf6 h1:Ass5FAjCCQ5WECPE9NN7ItZnKJ38i6sM8MCMNBGee5I=
-gvisor.dev/gvisor v0.0.0-20240110202538-8053cd8f0bf6/go.mod h1:10sU+Uh5KKNv1+2x2A0Gvzt8FjD3ASIhorV3YsauXhk=
-gvisor.dev/gvisor v0.0.0-20240327015314-08ed01b28587 h1:wH3g/qTCPlVBwkFktYuKNFJGeo7ctLNEjzrMlfPrVgE=
-gvisor.dev/gvisor v0.0.0-20240327015314-08ed01b28587/go.mod h1:NQHVAzMwvZ+Qe3ElSiHmq9RUm1MdNHpUZ52fiEqvn+0=
gvisor.dev/gvisor v0.0.0-20240409213450-87d8df37c71e h1:jpvBdtqDLzu2MZuruscr008NwJxiDidjFF4ZQq7YZbk=
gvisor.dev/gvisor v0.0.0-20240409213450-87d8df37c71e/go.mod h1:NQHVAzMwvZ+Qe3ElSiHmq9RUm1MdNHpUZ52fiEqvn+0=
gvisor.dev/gvisor v0.0.0-20240422234719-5cecdfbabd15 h1:zf/5V7KP7FL0G5GXg1zGPeaBkBG3CdJagSHbYZxsYKU=
gvisor.dev/gvisor v0.0.0-20240422234719-5cecdfbabd15/go.mod h1:sxc3Uvk/vHcd3tj7/DHVBoR5wvWT/MmRq2pj7HRJnwU=
honnef.co/go/tools v0.2.1/go.mod h1:lPVVZ2BS5TfnjLyizF7o7hv7j9/L+8cZY2hLyjP9cGY=
honnef.co/go/tools v0.2.2/go.mod h1:lPVVZ2BS5TfnjLyizF7o7hv7j9/L+8cZY2hLyjP9cGY=
-modernc.org/libc v1.40.1 h1:ZhRylEBcj3GyQbPVC8JxIg7SdrT4JOxIDJoUon0NfF8=
-modernc.org/libc v1.40.1/go.mod h1:YAXkAZ8ktnkCKaN9sw/UDeUVkGYJ/YquGO4FTi5nmHE=
+modernc.org/cc/v4 v4.20.0 h1:45Or8mQfbUqJOG9WaxvlFYOAQO0lQ5RvqBcFCXngjxk=
+modernc.org/cc/v4 v4.20.0/go.mod h1:HM7VJTZbUCR3rV8EYBi9wxnJ0ZBRiGE5OeGXNA0IsLQ=
+modernc.org/ccgo/v4 v4.16.0 h1:ofwORa6vx2FMm0916/CkZjpFPSR70VwTjUCe2Eg5BnA=
+modernc.org/ccgo/v4 v4.16.0/go.mod h1:dkNyWIjFrVIZ68DTo36vHK+6/ShBn4ysU61So6PIqCI=
+modernc.org/fileutil v1.3.0 h1:gQ5SIzK3H9kdfai/5x41oQiKValumqNTDXMvKo62HvE=
+modernc.org/fileutil v1.3.0/go.mod h1:XatxS8fZi3pS8/hKG2GH/ArUogfxjpEKs3Ku3aK4JyQ=
+modernc.org/gc/v2 v2.4.1 h1:9cNzOqPyMJBvrUipmynX0ZohMhcxPtMccYgGOJdOiBw=
+modernc.org/gc/v2 v2.4.1/go.mod h1:wzN5dK1AzVGoH6XOzc3YZ+ey/jPgYHLuVckd62P0GYU=
modernc.org/libc v1.49.3 h1:j2MRCRdwJI2ls/sGbeSk0t2bypOG/uvPZUsGQFDulqg=
modernc.org/libc v1.49.3/go.mod h1:yMZuGkn7pXbKfoT/M35gFJOAEdSKdxL0q64sF7KqCDo=
modernc.org/libc v1.50.2 h1:I0+3wlRvXmAEjAJvD7BhP1kmKHwkzV0rOcqFcD85u+0=
modernc.org/libc v1.50.2/go.mod h1:Fd8TZdfRorOd1vB0QCtYSHYAuzobS4xS3mhMGUkeVcA=
modernc.org/mathutil v1.6.0 h1:fRe9+AmYlaej+64JsEEhoWuAYBkOtQiMEU7n/XgfYi4=
modernc.org/mathutil v1.6.0/go.mod h1:Ui5Q9q1TR2gFm0AQRqQUaBWFLAhQpCwNcuhBOSedWPo=
-modernc.org/memory v1.7.2 h1:Klh90S215mmH8c9gO98QxQFsY+W451E8AnzjoE2ee1E=
-modernc.org/memory v1.7.2/go.mod h1:NO4NVCQy0N7ln+T9ngWqOQfi7ley4vpwvARR+Hjw95E=
modernc.org/memory v1.8.0 h1:IqGTL6eFMaDZZhEWwcREgeMXYwmW83LYW8cROZYkg+E=
modernc.org/memory v1.8.0/go.mod h1:XPZ936zp5OMKGWPqbD3JShgd/ZoQ7899TUuQqxY+peU=
-modernc.org/sqlite v1.28.0 h1:Zx+LyDDmXczNnEQdvPuEfcFVA2ZPyaD7UCZDjef3BHQ=
-modernc.org/sqlite v1.28.0/go.mod h1:Qxpazz0zH8Z1xCFyi5GSL3FzbtZ3fvbjmywNogldEW0=
+modernc.org/opt v0.1.3 h1:3XOZf2yznlhC+ibLltsDGzABUGVx8J6pnFMS3E4dcq4=
+modernc.org/opt v0.1.3/go.mod h1:WdSiB5evDcignE70guQKxYUl14mgWtbClRi5wmkkTX0=
+modernc.org/sortutil v1.2.0 h1:jQiD3PfS2REGJNzNCMMaLSp/wdMNieTbKX920Cqdgqc=
+modernc.org/sortutil v1.2.0/go.mod h1:TKU2s7kJMf1AE84OoiGppNHJwvB753OYfNl2WRb++Ss=
modernc.org/sqlite v1.29.6 h1:0lOXGrycJPptfHDuohfYgNqoe4hu+gYuN/pKgY5XjS4=
modernc.org/sqlite v1.29.6/go.mod h1:S02dvcmm7TnTRvGhv8IGYyLnIt7AS2KPaB1F/71p75U=
modernc.org/sqlite v1.29.8 h1:nGKglNx9K5v0As+zF0/Gcl1kMkmaU1XynYyq92PbsC8=
diff --git a/service/firewall/interception/interception_windows.go b/service/firewall/interception/interception_windows.go
index 71033c1a8..ab7535188 100644
--- a/service/firewall/interception/interception_windows.go
+++ b/service/firewall/interception/interception_windows.go
@@ -5,12 +5,16 @@ import (
"fmt"
"time"
- "github.com/safing/portmaster/service/firewall/interception/windowskext"
+ "github.com/safing/portbase/log"
+ kext1 "github.com/safing/portmaster/service/firewall/interception/windowskext"
+ kext2 "github.com/safing/portmaster/service/firewall/interception/windowskext2"
"github.com/safing/portmaster/service/network"
"github.com/safing/portmaster/service/network/packet"
"github.com/safing/portmaster/service/updates"
)
+var useOldKext = false
+
// start starts the interception.
func startInterception(packets chan packet.Packet) error {
kextFile, err := updates.GetPlatformFile("kext/portmaster-kext.sys")
@@ -18,51 +22,142 @@ func startInterception(packets chan packet.Packet) error {
return fmt.Errorf("interception: could not get kext sys: %s", err)
}
- err = windowskext.Init(kextFile.Path())
+ err = kext2.Init(kextFile.Path())
if err != nil {
return fmt.Errorf("interception: could not init windows kext: %s", err)
}
- err = windowskext.Start()
+ err = kext2.Start()
if err != nil {
return fmt.Errorf("interception: could not start windows kext: %s", err)
}
- // Start packet handler.
- module.StartServiceWorker("kext packet handler", 0, func(ctx context.Context) error {
- windowskext.Handler(ctx, packets)
- return nil
- })
+ version, err := kext2.GetVersion()
+ if err != nil {
+ return fmt.Errorf("interception: failed to read version: %s", err)
+ }
+ log.Debugf("Kext version: %s", version.String())
+
+ if version.Major < 2 {
+ useOldKext = true
+
+ // Transfer ownership.
+ kext1.SetKextHandler(kext2.GetKextHandle())
+ kext1.SetKextService(kext2.GetKextServiceHandle(), kextFile.Path())
+
+ // Start packet handler.
+ module.StartServiceWorker("kext packet handler", 0, func(ctx context.Context) error {
+ kext1.Handler(ctx, packets)
+ return nil
+ })
+
+ // Start bandwidth stats monitor.
+ module.StartServiceWorker("kext bandwidth stats monitor", 0, func(ctx context.Context) error {
+ return kext1.BandwidthStatsWorker(ctx, 1*time.Second, BandwidthUpdates)
+ })
+ } else {
+
+ // Start packet handler.
+ module.StartServiceWorker("kext packet handler", 0, func(ctx context.Context) error {
+ kext2.Handler(ctx, packets, BandwidthUpdates)
+ return nil
+ })
+
+ // Start bandwidth stats monitor.
+ module.StartServiceWorker("kext bandwidth request worker", 0, func(ctx context.Context) error {
+ timer := time.NewTicker(1 * time.Second)
+ defer timer.Stop()
+ for {
+ select {
+ case <-timer.C:
+ err := kext2.SendBandwidthStatsRequest()
+ if err != nil {
+ return err
+ }
+ case <-ctx.Done():
+ return nil
+ }
- // Start bandwidth stats monitor.
- module.StartServiceWorker("kext bandwidth stats monitor", 0, func(ctx context.Context) error {
- return windowskext.BandwidthStatsWorker(ctx, 1*time.Second, BandwidthUpdates)
- })
+ }
+ })
+
+ // Start kext logging. The worker will periodically send request to the kext to send logs.
+ module.StartServiceWorker("kext log request worker", 0, func(ctx context.Context) error {
+ timer := time.NewTicker(1 * time.Second)
+ defer timer.Stop()
+ for {
+ select {
+ case <-timer.C:
+ err := kext2.SendLogRequest()
+ if err != nil {
+ return err
+ }
+ case <-ctx.Done():
+ return nil
+ }
+
+ }
+ })
+
+ module.StartServiceWorker("kext clean ended connection worker", 0, func(ctx context.Context) error {
+ timer := time.NewTicker(30 * time.Second)
+ defer timer.Stop()
+ for {
+ select {
+ case <-timer.C:
+ err := kext2.SendCleanEndedConnection()
+ if err != nil {
+ return err
+ }
+ case <-ctx.Done():
+ return nil
+ }
+
+ }
+ })
+ }
return nil
}
// stop starts the interception.
func stopInterception() error {
- return windowskext.Stop()
+ if useOldKext {
+ return kext1.Stop()
+ }
+ return kext2.Stop()
}
// ResetVerdictOfAllConnections resets all connections so they are forced to go thought the firewall again.
func ResetVerdictOfAllConnections() error {
- return windowskext.ClearCache()
+ if useOldKext {
+ return kext1.ClearCache()
+ }
+ return kext2.ClearCache()
}
// UpdateVerdictOfConnection updates the verdict of the given connection in the kernel extension.
func UpdateVerdictOfConnection(conn *network.Connection) error {
- return windowskext.UpdateVerdict(conn)
+ if useOldKext {
+ return kext1.UpdateVerdict(conn)
+ }
+ return kext2.UpdateVerdict(conn)
}
// GetKextVersion returns the version of the kernel extension.
func GetKextVersion() (string, error) {
- version, err := windowskext.GetVersion()
- if err != nil {
- return "", err
+ if useOldKext {
+ version, err := kext1.GetVersion()
+ if err != nil {
+ return "", err
+ }
+ return version.String(), nil
+ } else {
+ version, err := kext2.GetVersion()
+ if err != nil {
+ return "", err
+ }
+ return version.String(), nil
}
- return version.String(), nil
}
diff --git a/service/firewall/interception/windowskext/bandwidth_stats.go b/service/firewall/interception/windowskext/bandwidth_stats.go
index f1fb856bd..a29e50d99 100644
--- a/service/firewall/interception/windowskext/bandwidth_stats.go
+++ b/service/firewall/interception/windowskext/bandwidth_stats.go
@@ -81,7 +81,7 @@ func reportBandwidth(ctx context.Context, bandwidthUpdates chan *packet.Bandwidt
return nil
}
-func StartBandwithConsoleLogger() {
+func StartBandwidthConsoleLogger() {
go func() {
ticker := time.NewTicker(2 * time.Second)
defer ticker.Stop()
diff --git a/service/firewall/interception/windowskext/kext.go b/service/firewall/interception/windowskext/kext.go
index 7699c35a3..34badd6d7 100644
--- a/service/firewall/interception/windowskext/kext.go
+++ b/service/firewall/interception/windowskext/kext.go
@@ -76,6 +76,15 @@ func Start() error {
return nil
}
+func SetKextHandler(handle windows.Handle) {
+ kextHandle = handle
+}
+
+func SetKextService(handle windows.Handle, path string) {
+ service = &KextService{handle: handle}
+ driverPath = path
+}
+
// Stop intercepting.
func Stop() error {
// Prepare kernel for shutdown
diff --git a/service/firewall/interception/windowskext/service.go b/service/firewall/interception/windowskext/service.go
index 399407bbf..e3e4ac2a9 100644
--- a/service/firewall/interception/windowskext/service.go
+++ b/service/firewall/interception/windowskext/service.go
@@ -24,6 +24,7 @@ func createKextService(driverName string, driverPath string) (*KextService, erro
}
defer windows.CloseServiceHandle(manager)
+ // Convert the driver name to a UTF16 string
driverNameU16, err := syscall.UTF16FromString(driverName)
if err != nil {
return nil, fmt.Errorf("failed to convert driver name to UTF16 string: %w", err)
diff --git a/service/firewall/interception/windowskext2/doc.go b/service/firewall/interception/windowskext2/doc.go
new file mode 100644
index 000000000..c68a942fe
--- /dev/null
+++ b/service/firewall/interception/windowskext2/doc.go
@@ -0,0 +1,4 @@
+// +build windows
+
+// Package windowskext provides network interception capabilities on windows via the Portmaster Kernel Extension.
+package windowskext
diff --git a/service/firewall/interception/windowskext2/handler.go b/service/firewall/interception/windowskext2/handler.go
new file mode 100644
index 000000000..bb6348dd2
--- /dev/null
+++ b/service/firewall/interception/windowskext2/handler.go
@@ -0,0 +1,195 @@
+//go:build windows
+// +build windows
+
+package windowskext
+
+import (
+ "context"
+ "fmt"
+ "net"
+ "time"
+
+ "github.com/safing/portmaster/service/process"
+
+ "github.com/tevino/abool"
+
+ "github.com/safing/portbase/log"
+ "github.com/safing/portmaster/service/network/packet"
+)
+
+type VersionInfo struct {
+ Major uint8
+ Minor uint8
+ Revision uint8
+ Build uint8
+}
+
+func (v *VersionInfo) String() string {
+ return fmt.Sprintf("%d.%d.%d.%d", v.Major, v.Minor, v.Revision, v.Build)
+}
+
+// Handler transforms received packets to the Packet interface.
+func Handler(ctx context.Context, packets chan packet.Packet, bandwidthUpdate chan *packet.BandwidthUpdate) {
+ for {
+ packetInfo, err := RecvVerdictRequest()
+ if err != nil {
+ log.Warningf("failed to get packet from windows kext: %s", err)
+ return
+ }
+
+ switch {
+ case packetInfo.ConnectionV4 != nil:
+ {
+ // log.Tracef("packet: %+v", packetInfo.ConnectionV4)
+ conn := packetInfo.ConnectionV4
+ // New Packet
+ newPacket := &Packet{
+ verdictRequest: conn.ID,
+ payload: conn.Payload,
+ verdictSet: abool.NewBool(false),
+ }
+ info := newPacket.Info()
+ info.Inbound = conn.Direction > 0
+ info.InTunnel = false
+ info.Protocol = packet.IPProtocol(conn.Protocol)
+ info.PID = int(conn.ProcessID)
+ info.SeenAt = time.Now()
+
+ // Check PID
+ if info.PID == 0 {
+ // Windows does not have zero PIDs.
+ // Set to UndefinedProcessID.
+ info.PID = process.UndefinedProcessID
+ }
+
+ // Set IP version
+ info.Version = packet.IPv4
+
+ // Set IPs
+ if info.Inbound {
+ // Inbound
+ info.Src = conn.RemoteIP[:]
+ info.Dst = conn.LocalIP[:]
+ } else {
+ // Outbound
+ info.Src = conn.LocalIP[:]
+ info.Dst = conn.RemoteIP[:]
+ }
+
+ // Set Ports
+ if info.Inbound {
+ // Inbound
+ info.SrcPort = conn.RemotePort
+ info.DstPort = conn.LocalPort
+ } else {
+ // Outbound
+ info.SrcPort = conn.LocalPort
+ info.DstPort = conn.RemotePort
+ }
+
+ packets <- newPacket
+ }
+ case packetInfo.ConnectionV6 != nil:
+ {
+ // log.Tracef("packet: %+v", packetInfo.ConnectionV6)
+ conn := packetInfo.ConnectionV6
+ // New Packet
+ newPacket := &Packet{
+ verdictRequest: conn.ID,
+ payload: conn.Payload,
+ verdictSet: abool.NewBool(false),
+ }
+ info := newPacket.Info()
+ info.Inbound = conn.Direction > 0
+ info.InTunnel = false
+ info.Protocol = packet.IPProtocol(conn.Protocol)
+ info.PID = int(conn.ProcessID)
+ info.SeenAt = time.Now()
+
+ // Check PID
+ if info.PID == 0 {
+ // Windows does not have zero PIDs.
+ // Set to UndefinedProcessID.
+ info.PID = process.UndefinedProcessID
+ }
+
+ // Set IP version
+ info.Version = packet.IPv6
+
+ // Set IPs
+ if info.Inbound {
+ // Inbound
+ info.Src = conn.RemoteIP[:]
+ info.Dst = conn.LocalIP[:]
+ } else {
+ // Outbound
+ info.Src = conn.LocalIP[:]
+ info.Dst = conn.RemoteIP[:]
+ }
+
+ // Set Ports
+ if info.Inbound {
+ // Inbound
+ info.SrcPort = conn.RemotePort
+ info.DstPort = conn.LocalPort
+ } else {
+ // Outbound
+ info.SrcPort = conn.LocalPort
+ info.DstPort = conn.RemotePort
+ }
+
+ packets <- newPacket
+ }
+ case packetInfo.LogLine != nil:
+ {
+ line := packetInfo.LogLine
+ switch line.Severity {
+ case byte(log.DebugLevel):
+ log.Debugf("kext: %s", line.Line)
+ case byte(log.InfoLevel):
+ log.Infof("kext: %s", line.Line)
+ case byte(log.WarningLevel):
+ log.Warningf("kext: %s", line.Line)
+ case byte(log.ErrorLevel):
+ log.Errorf("kext: %s", line.Line)
+ case byte(log.CriticalLevel):
+ log.Criticalf("kext: %s", line.Line)
+ }
+ }
+ case packetInfo.BandwidthStats != nil:
+ {
+ bandwidthStats := packetInfo.BandwidthStats
+ for _, stat := range bandwidthStats.ValuesV4 {
+ connID := packet.CreateConnectionID(
+ packet.IPProtocol(bandwidthStats.Protocol),
+ net.IP(stat.LocalIP[:]), stat.LocalPort,
+ net.IP(stat.RemoteIP[:]), stat.RemotePort,
+ false,
+ )
+ update := &packet.BandwidthUpdate{
+ ConnID: connID,
+ BytesReceived: stat.ReceivedBytes,
+ BytesSent: stat.TransmittedBytes,
+ Method: packet.Additive,
+ }
+ bandwidthUpdate <- update
+ }
+ for _, stat := range bandwidthStats.ValuesV6 {
+ connID := packet.CreateConnectionID(
+ packet.IPProtocol(bandwidthStats.Protocol),
+ net.IP(stat.LocalIP[:]), stat.LocalPort,
+ net.IP(stat.RemoteIP[:]), stat.RemotePort,
+ false,
+ )
+ update := &packet.BandwidthUpdate{
+ ConnID: connID,
+ BytesReceived: stat.ReceivedBytes,
+ BytesSent: stat.TransmittedBytes,
+ Method: packet.Additive,
+ }
+ bandwidthUpdate <- update
+ }
+ }
+ }
+ }
+}
diff --git a/service/firewall/interception/windowskext2/kext.go b/service/firewall/interception/windowskext2/kext.go
new file mode 100644
index 000000000..fd6adb721
--- /dev/null
+++ b/service/firewall/interception/windowskext2/kext.go
@@ -0,0 +1,166 @@
+//go:build windows
+// +build windows
+
+package windowskext
+
+import (
+ "fmt"
+
+ "github.com/safing/portbase/log"
+ "github.com/safing/portmaster/service/network"
+ "github.com/safing/portmaster/windows_kext/kextinterface"
+ "golang.org/x/sys/windows"
+)
+
+// Package errors
+var (
+ driverPath string
+
+ service *kextinterface.KextService
+ kextFile *kextinterface.KextFile
+)
+
+const (
+ driverName = "PortmasterKext"
+)
+
+func Init(path string) error {
+ driverPath = path
+ return nil
+}
+
+// Start intercepting.
+func Start() error {
+ // initialize and start driver service
+ var err error
+ service, err = kextinterface.CreateKextService(driverName, driverPath)
+ if err != nil {
+ return fmt.Errorf("failed to create service: %w", err)
+ }
+
+ // Start service and open file
+ service.Start(true)
+ kextFile, err = service.OpenFile(1024)
+
+ if err != nil {
+ return fmt.Errorf("failed to open driver: %w", err)
+ }
+
+ return nil
+}
+
+func GetKextHandle() windows.Handle {
+ return kextFile.GetHandle()
+}
+
+func GetKextServiceHandle() windows.Handle {
+ return service.GetHandle()
+}
+
+// Stop intercepting.
+func Stop() error {
+ // Prepare kernel for shutdown
+ err := shutdownRequest()
+ if err != nil {
+ log.Warningf("winkext: shutdown request failed: %s", err)
+ }
+ // Close the interface to the driver. Driver will continue to run.
+ err = kextFile.Close()
+ if err != nil {
+ log.Warningf("winkext: failed to close kext file: %s", err)
+ }
+
+ // Stop and delete the driver.
+ err = service.Stop(true)
+ if err != nil {
+ log.Warningf("winkext: failed to stop kernel service: %s", err)
+ }
+
+ err = service.Delete()
+ if err != nil {
+ log.Warningf("winkext: failed to delete kernel service: %s", err)
+ }
+ return nil
+}
+
+// Sends a shutdown request.
+func shutdownRequest() error {
+ return kextinterface.SendShutdownCommand(kextFile)
+}
+
+// Send request for logs of the kext.
+func SendLogRequest() error {
+ return kextinterface.SendGetLogsCommand(kextFile)
+}
+
+func SendBandwidthStatsRequest() error {
+ return kextinterface.SendGetBandwidthStatsCommand(kextFile)
+}
+
+func SendPrintMemoryStatsCommand() error {
+ return kextinterface.SendPrintMemoryStatsCommand(kextFile)
+}
+
+func SendCleanEndedConnection() error {
+ return kextinterface.SendCleanEndedConnectionsCommand(kextFile)
+}
+
+// RecvVerdictRequest waits for the next verdict request from the kext. If a timeout is reached, both *VerdictRequest and error will be nil.
+func RecvVerdictRequest() (*kextinterface.Info, error) {
+ return kextinterface.RecvInfo(kextFile)
+}
+
+// SetVerdict sets the verdict for a packet and/or connection.
+func SetVerdict(pkt *Packet, verdict kextinterface.KextVerdict) error {
+ verdictCommand := kextinterface.Verdict{ID: pkt.verdictRequest, Verdict: uint8(verdict)}
+ return kextinterface.SendVerdictCommand(kextFile, verdictCommand)
+}
+
+// Clears the internal connection cache.
+func ClearCache() error {
+ return kextinterface.SendClearCacheCommand(kextFile)
+}
+
+// Updates a specific connection verdict.
+func UpdateVerdict(conn *network.Connection) error {
+ if conn.IPVersion == 4 {
+ update := kextinterface.UpdateV4{
+ Protocol: conn.Entity.Protocol,
+ LocalAddress: [4]byte(conn.LocalIP),
+ LocalPort: conn.LocalPort,
+ RemoteAddress: [4]byte(conn.Entity.IP),
+ RemotePort: conn.Entity.Port,
+ Verdict: uint8(conn.Verdict),
+ }
+
+ return kextinterface.SendUpdateV4Command(kextFile, update)
+ } else if conn.IPVersion == 6 {
+ update := kextinterface.UpdateV6{
+ Protocol: conn.Entity.Protocol,
+ LocalAddress: [16]byte(conn.LocalIP),
+ LocalPort: conn.LocalPort,
+ RemoteAddress: [16]byte(conn.Entity.IP),
+ RemotePort: conn.Entity.Port,
+ Verdict: uint8(conn.Verdict),
+ }
+
+ return kextinterface.SendUpdateV6Command(kextFile, update)
+ }
+ return nil
+}
+
+// Returns the kext version.
+func GetVersion() (*VersionInfo, error) {
+ data, err := kextinterface.ReadVersion(kextFile)
+ if err != nil {
+ return nil, err
+ }
+
+ version := &VersionInfo{
+ Major: data[0],
+ Minor: data[1],
+ Revision: data[2],
+ Build: data[3],
+ }
+ return version, nil
+}
diff --git a/service/firewall/interception/windowskext2/packet.go b/service/firewall/interception/windowskext2/packet.go
new file mode 100644
index 000000000..3ea9c0095
--- /dev/null
+++ b/service/firewall/interception/windowskext2/packet.go
@@ -0,0 +1,132 @@
+//go:build windows
+// +build windows
+
+package windowskext
+
+import (
+ "sync"
+
+ "github.com/tevino/abool"
+
+ "github.com/safing/portbase/log"
+ "github.com/safing/portmaster/service/network/packet"
+ "github.com/safing/portmaster/windows_kext/kextinterface"
+)
+
+// Packet represents an IP packet.
+type Packet struct {
+ packet.Base
+
+ verdictRequest uint64
+ payload []byte
+ verdictSet *abool.AtomicBool
+
+ payloadLoaded bool
+ lock sync.Mutex
+}
+
+// FastTrackedByIntegration returns whether the packet has been fast-track
+// accepted by the OS integration.
+func (pkt *Packet) FastTrackedByIntegration() bool {
+ return false
+}
+
+// InfoOnly returns whether the packet is informational only and does not
+// represent an actual packet.
+func (pkt *Packet) InfoOnly() bool {
+ return false
+}
+
+// ExpectInfo returns whether the next packet is expected to be informational only.
+func (pkt *Packet) ExpectInfo() bool {
+ return false
+}
+
+// GetPayload returns the full raw packet.
+func (pkt *Packet) LoadPacketData() error {
+ pkt.lock.Lock()
+ defer pkt.lock.Unlock()
+
+ if !pkt.payloadLoaded {
+ pkt.payloadLoaded = true
+
+ if len(pkt.payload) > 0 {
+ err := packet.Parse(pkt.payload, &pkt.Base)
+ if err != nil {
+ log.Tracef("payload: %#v", pkt.payload)
+ log.Tracer(pkt.Ctx()).Warningf("windowskext: failed to parse payload: %s", err)
+ return packet.ErrFailedToLoadPayload
+ }
+ }
+ }
+
+ if len(pkt.Raw()) == 0 {
+ return packet.ErrFailedToLoadPayload
+ }
+
+ return nil
+}
+
+// Accept accepts the packet.
+func (pkt *Packet) Accept() error {
+ if pkt.verdictSet.SetToIf(false, true) {
+ return SetVerdict(pkt, kextinterface.VerdictAccept)
+ }
+ return nil
+}
+
+// Block blocks the packet.
+func (pkt *Packet) Block() error {
+ if pkt.verdictSet.SetToIf(false, true) {
+ return SetVerdict(pkt, kextinterface.VerdictBlock)
+ }
+ return nil
+}
+
+// Drop drops the packet.
+func (pkt *Packet) Drop() error {
+ if pkt.verdictSet.SetToIf(false, true) {
+ return SetVerdict(pkt, kextinterface.VerdictDrop)
+ }
+ return nil
+}
+
+// PermanentAccept permanently accepts connection (and the current packet).
+func (pkt *Packet) PermanentAccept() error {
+ if pkt.verdictSet.SetToIf(false, true) {
+ return SetVerdict(pkt, kextinterface.VerdictPermanentAccept)
+ }
+ return nil
+}
+
+// PermanentBlock permanently blocks connection (and the current packet).
+func (pkt *Packet) PermanentBlock() error {
+ if pkt.verdictSet.SetToIf(false, true) {
+ return SetVerdict(pkt, kextinterface.VerdictPermanentBlock)
+ }
+ return nil
+}
+
+// PermanentDrop permanently drops connection (and the current packet).
+func (pkt *Packet) PermanentDrop() error {
+ if pkt.verdictSet.SetToIf(false, true) {
+ return SetVerdict(pkt, kextinterface.VerdictPermanentDrop)
+ }
+ return nil
+}
+
+// RerouteToNameserver permanently reroutes the connection to the local nameserver (and the current packet).
+func (pkt *Packet) RerouteToNameserver() error {
+ if pkt.verdictSet.SetToIf(false, true) {
+ return SetVerdict(pkt, kextinterface.VerdictRerouteToNameserver)
+ }
+ return nil
+}
+
+// RerouteToTunnel permanently reroutes the connection to the local tunnel entrypoint (and the current packet).
+func (pkt *Packet) RerouteToTunnel() error {
+ if pkt.verdictSet.SetToIf(false, true) {
+ return SetVerdict(pkt, kextinterface.VerdictRerouteToTunnel)
+ }
+ return nil
+}
diff --git a/service/firewall/interception/windowskext2/service.go b/service/firewall/interception/windowskext2/service.go
new file mode 100644
index 000000000..5c939f625
--- /dev/null
+++ b/service/firewall/interception/windowskext2/service.go
@@ -0,0 +1,10 @@
+//go:build windows
+// +build windows
+
+package windowskext
+
+import "github.com/safing/portmaster/windows_kext/kextinterface"
+
+func createKextService(driverName string, driverPath string) (*kextinterface.KextService, error) {
+ return kextinterface.CreateKextService(driverName, driverPath)
+}
diff --git a/service/network/connection.go b/service/network/connection.go
index 457a2283f..b83ee5423 100644
--- a/service/network/connection.go
+++ b/service/network/connection.go
@@ -424,6 +424,7 @@ func NewIncompleteConnection(pkt packet.Packet) *Connection {
IPProtocol: info.Protocol,
Started: info.SeenAt.Unix(),
PID: info.PID,
+ Inbound: info.Inbound,
dataComplete: abool.NewBool(false),
}
conn.lastSeen.Store(conn.Started)
diff --git a/windows_kext/.gitignore b/windows_kext/.gitignore
new file mode 100644
index 000000000..c42308415
--- /dev/null
+++ b/windows_kext/.gitignore
@@ -0,0 +1,400 @@
+## Ignore Visual Studio temporary files, build results, and
+## files generated by popular Visual Studio add-ons.
+##
+## Get latest from https://github.com/github/gitignore/blob/main/VisualStudio.gitignore
+
+# User-specific files
+*.rsuser
+*.suo
+*.user
+*.userosscache
+*.sln.docstates
+
+# User-specific files (MonoDevelop/Xamarin Studio)
+*.userprefs
+
+# Mono auto generated files
+mono_crash.*
+
+# Build results
+[Dd]ebug/
+[Dd]ebugPublic/
+*/**/[Rr]elease/
+*/**/[Rr]eleases/
+bld/
+[Bb]in/
+[Oo]bj/
+[Ll]og/
+[Ll]ogs/
+
+# Visual Studio 2015/2017 cache/options directory
+.vs/
+# Uncomment if you have tasks that create the project's static files in wwwroot
+#wwwroot/
+
+# Visual Studio 2017 auto generated files
+Generated\ Files/
+
+# MSTest test Results
+[Tt]est[Rr]esult*/
+[Bb]uild[Ll]og.*
+
+# NUnit
+*.VisualState.xml
+TestResult.xml
+nunit-*.xml
+
+# Build Results of an ATL Project
+[Dd]ebugPS/
+[Rr]eleasePS/
+dlldata.c
+
+# Benchmark Results
+BenchmarkDotNet.Artifacts/
+
+# .NET Core
+project.lock.json
+project.fragment.lock.json
+artifacts/
+
+# ASP.NET Scaffolding
+ScaffoldingReadMe.txt
+
+# StyleCop
+StyleCopReport.xml
+
+# Files built by Visual Studio
+*_i.c
+*_p.c
+*_h.h
+*.ilk
+*.meta
+*.obj
+*.iobj
+*.pch
+*.pdb
+*.ipdb
+*.pgc
+*.pgd
+*.rsp
+*.sbr
+*.tlb
+*.tli
+*.tlh
+*.tmp
+*.tmp_proj
+*_wpftmp.csproj
+*.log
+*.tlog
+*.vspscc
+*.vssscc
+.builds
+*.pidb
+*.svclog
+*.scc
+*.exe
+
+# Chutzpah Test files
+_Chutzpah*
+
+# Visual C++ cache files
+ipch/
+*.aps
+*.ncb
+*.opendb
+*.opensdf
+*.sdf
+*.cachefile
+*.VC.db
+*.VC.VC.opendb
+
+# Visual Studio profiler
+*.psess
+*.vsp
+*.vspx
+*.sap
+
+# Visual Studio Trace Files
+*.e2e
+
+# TFS 2012 Local Workspace
+$tf/
+
+# Guidance Automation Toolkit
+*.gpState
+
+# ReSharper is a .NET coding add-in
+_ReSharper*/
+*.[Rr]e[Ss]harper
+*.DotSettings.user
+
+# TeamCity is a build add-in
+_TeamCity*
+
+# DotCover is a Code Coverage Tool
+*.dotCover
+
+# AxoCover is a Code Coverage Tool
+.axoCover/*
+!.axoCover/settings.json
+
+# Coverlet is a free, cross platform Code Coverage Tool
+coverage*.json
+coverage*.xml
+coverage*.info
+
+# Visual Studio code coverage results
+*.coverage
+*.coveragexml
+
+# NCrunch
+_NCrunch_*
+.*crunch*.local.xml
+nCrunchTemp_*
+
+# MightyMoose
+*.mm.*
+AutoTest.Net/
+
+# Web workbench (sass)
+.sass-cache/
+
+# Installshield output folder
+[Ee]xpress/
+
+# DocProject is a documentation generator add-in
+DocProject/buildhelp/
+DocProject/Help/*.HxT
+DocProject/Help/*.HxC
+DocProject/Help/*.hhc
+DocProject/Help/*.hhk
+DocProject/Help/*.hhp
+DocProject/Help/Html2
+DocProject/Help/html
+
+# Click-Once directory
+publish/
+
+# Publish Web Output
+*.[Pp]ublish.xml
+*.azurePubxml
+# Note: Comment the next line if you want to checkin your web deploy settings,
+# but database connection strings (with potential passwords) will be unencrypted
+*.pubxml
+*.publishproj
+
+# Microsoft Azure Web App publish settings. Comment the next line if you want to
+# checkin your Azure Web App publish settings, but sensitive information contained
+# in these scripts will be unencrypted
+PublishScripts/
+
+# NuGet Packages
+*.nupkg
+# NuGet Symbol Packages
+*.snupkg
+# The packages folder can be ignored because of Package Restore
+**/[Pp]ackages/*
+# except build/, which is used as an MSBuild target.
+!**/[Pp]ackages/build/
+# Uncomment if necessary however generally it will be regenerated when needed
+#!**/[Pp]ackages/repositories.config
+# NuGet v3's project.json files produces more ignorable files
+*.nuget.props
+*.nuget.targets
+
+# Microsoft Azure Build Output
+csx/
+*.build.csdef
+
+# Microsoft Azure Emulator
+ecf/
+rcf/
+
+# Windows Store app package directories and files
+AppPackages/
+BundleArtifacts/
+Package.StoreAssociation.xml
+_pkginfo.txt
+*.appx
+*.appxbundle
+*.appxupload
+
+# Visual Studio cache files
+# files ending in .cache can be ignored
+*.[Cc]ache
+# but keep track of directories ending in .cache
+!?*.[Cc]ache/
+
+# Others
+ClientBin/
+~$*
+*~
+*.dbmdl
+*.dbproj.schemaview
+*.jfm
+*.pfx
+*.publishsettings
+orleans.codegen.cs
+
+# Including strong name files can present a security risk
+# (https://github.com/github/gitignore/pull/2483#issue-259490424)
+#*.snk
+
+# Since there are multiple workflows, uncomment next line to ignore bower_components
+# (https://github.com/github/gitignore/pull/1529#issuecomment-104372622)
+#bower_components/
+
+# RIA/Silverlight projects
+Generated_Code/
+
+# Backup & report files from converting an old project file
+# to a newer Visual Studio version. Backup files are not needed,
+# because we have git ;-)
+_UpgradeReport_Files/
+Backup*/
+UpgradeLog*.XML
+UpgradeLog*.htm
+ServiceFabricBackup/
+*.rptproj.bak
+
+# SQL Server files
+*.mdf
+*.ldf
+*.ndf
+
+# Business Intelligence projects
+*.rdl.data
+*.bim.layout
+*.bim_*.settings
+*.rptproj.rsuser
+*- [Bb]ackup.rdl
+*- [Bb]ackup ([0-9]).rdl
+*- [Bb]ackup ([0-9][0-9]).rdl
+
+# Microsoft Fakes
+FakesAssemblies/
+
+# GhostDoc plugin setting file
+*.GhostDoc.xml
+
+# Node.js Tools for Visual Studio
+.ntvs_analysis.dat
+node_modules/
+
+# Visual Studio 6 build log
+*.plg
+
+# Visual Studio 6 workspace options file
+*.opt
+
+# Visual Studio 6 auto-generated workspace file (contains which files were open etc.)
+*.vbw
+
+# Visual Studio 6 auto-generated project file (contains which files were open etc.)
+*.vbp
+
+# Visual Studio 6 workspace and project file (working project files containing files to include in project)
+*.dsw
+*.dsp
+
+# Visual Studio 6 technical files
+*.ncb
+*.aps
+
+# Visual Studio LightSwitch build output
+**/*.HTMLClient/GeneratedArtifacts
+**/*.DesktopClient/GeneratedArtifacts
+**/*.DesktopClient/ModelManifest.xml
+**/*.Server/GeneratedArtifacts
+**/*.Server/ModelManifest.xml
+_Pvt_Extensions
+
+# Paket dependency manager
+.paket/paket.exe
+paket-files/
+
+# FAKE - F# Make
+.fake/
+
+# CodeRush personal settings
+.cr/personal
+
+# Python Tools for Visual Studio (PTVS)
+__pycache__/
+*.pyc
+
+# Cake - Uncomment if you are using it
+# tools/**
+# !tools/packages.config
+
+# Tabs Studio
+*.tss
+
+# Telerik's JustMock configuration file
+*.jmconfig
+
+# BizTalk build output
+*.btp.cs
+*.btm.cs
+*.odx.cs
+*.xsd.cs
+
+# OpenCover UI analysis results
+OpenCover/
+
+# Azure Stream Analytics local run output
+ASALocalRun/
+
+# MSBuild Binary and Structured Log
+*.binlog
+
+# NVidia Nsight GPU debugger configuration file
+*.nvuser
+
+# MFractors (Xamarin productivity tool) working folder
+.mfractor/
+
+# Local History for Visual Studio
+.localhistory/
+
+# Visual Studio History (VSHistory) files
+.vshistory/
+
+# BeatPulse healthcheck temp database
+healthchecksdb
+
+# Backup folder for Package Reference Convert tool in Visual Studio 2017
+MigrationBackup/
+
+# Ionide (cross platform F# VS Code tools) working folder
+.ionide/
+
+# Fody - auto-generated XML schema
+FodyWeavers.xsd
+
+# VS Code files for those working on multiple tools
+.vscode/*
+!.vscode/settings.json
+!.vscode/tasks.json
+!.vscode/launch.json
+!.vscode/extensions.json
+*.code-workspace
+
+# Local History for Visual Studio Code
+.history/
+
+# Windows Installer files from build outputs
+*.cab
+*.msi
+*.msix
+*.msm
+*.msp
+
+# JetBrains Rider
+*.sln.iml
+pm_kext/RCa04400
+pm_kext/RCa05788
+pm_kext/RCa06452
+target
+kext.sys
+release/build
diff --git a/windows_kext/Cargo.lock b/windows_kext/Cargo.lock
new file mode 100644
index 000000000..29a6169a0
--- /dev/null
+++ b/windows_kext/Cargo.lock
@@ -0,0 +1,417 @@
+# This file is automatically @generated by Cargo.
+# It is not intended for manual editing.
+version = 3
+
+[[package]]
+name = "anyhow"
+version = "1.0.75"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a4668cab20f66d8d020e1fbc0ebe47217433c1b6c8f2040faf858554e394ace6"
+
+[[package]]
+name = "cfg-if"
+version = "1.0.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
+
+[[package]]
+name = "getrandom"
+version = "0.2.10"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "be4136b2a15dd319360be1c07d9933517ccf0be8f16bf62a3bee4f0d618df427"
+dependencies = [
+ "cfg-if",
+ "libc",
+ "wasi",
+]
+
+[[package]]
+name = "glob"
+version = "0.3.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d2fabcfbdc87f4758337ca535fb41a6d701b65693ce38287d856d1674551ec9b"
+
+[[package]]
+name = "heck"
+version = "0.3.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6d621efb26863f0e9924c6ac577e8275e5e6b77455db64ffa6c65c904e9e132c"
+dependencies = [
+ "unicode-segmentation",
+]
+
+[[package]]
+name = "include_dir"
+version = "0.6.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "24b56e147e6187d61e9d0f039f10e070d0c0a887e24fe0bb9ca3f29bfde62cab"
+dependencies = [
+ "glob",
+ "include_dir_impl",
+ "proc-macro-hack",
+]
+
+[[package]]
+name = "include_dir_impl"
+version = "0.6.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0a0c890c85da4bab7bce4204c707396bbd3c6c8a681716a51c8814cfc2b682df"
+dependencies = [
+ "anyhow",
+ "proc-macro-hack",
+ "proc-macro2",
+ "quote",
+ "syn 1.0.109",
+]
+
+[[package]]
+name = "libc"
+version = "0.2.147"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b4668fb0ea861c1df094127ac5f1da3409a82116a4ba74fca2e58ef927159bb3"
+
+[[package]]
+name = "once_cell"
+version = "1.18.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "dd8b5dd2ae5ed71462c540258bedcb51965123ad7e7ccf4b9a8cafaa4a63576d"
+
+[[package]]
+name = "phf"
+version = "0.10.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "fabbf1ead8a5bcbc20f5f8b939ee3f5b0f6f281b6ad3468b84656b658b455259"
+dependencies = [
+ "phf_macros",
+ "phf_shared",
+ "proc-macro-hack",
+]
+
+[[package]]
+name = "phf_generator"
+version = "0.10.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5d5285893bb5eb82e6aaf5d59ee909a06a16737a8970984dd7746ba9283498d6"
+dependencies = [
+ "phf_shared",
+ "rand",
+]
+
+[[package]]
+name = "phf_macros"
+version = "0.10.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "58fdf3184dd560f160dd73922bea2d5cd6e8f064bf4b13110abd81b03697b4e0"
+dependencies = [
+ "phf_generator",
+ "phf_shared",
+ "proc-macro-hack",
+ "proc-macro2",
+ "quote",
+ "syn 1.0.109",
+]
+
+[[package]]
+name = "phf_shared"
+version = "0.10.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b6796ad771acdc0123d2a88dc428b5e38ef24456743ddb1744ed628f9815c096"
+dependencies = [
+ "siphasher",
+]
+
+[[package]]
+name = "portmaster-windows-kext"
+version = "0.1.0"
+dependencies = [
+ "serde",
+ "serde-generate",
+ "serde-reflection",
+ "widestring",
+ "winapi",
+ "windows-sys",
+]
+
+[[package]]
+name = "ppv-lite86"
+version = "0.2.17"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de"
+
+[[package]]
+name = "proc-macro-hack"
+version = "0.5.20+deprecated"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "dc375e1527247fe1a97d8b7156678dfe7c1af2fc075c9a4db3690ecd2a148068"
+
+[[package]]
+name = "proc-macro2"
+version = "1.0.66"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "18fb31db3f9bddb2ea821cde30a9f70117e3f119938b5ee630b7403aa6e2ead9"
+dependencies = [
+ "unicode-ident",
+]
+
+[[package]]
+name = "quote"
+version = "1.0.33"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5267fca4496028628a95160fc423a33e8b2e6af8a5302579e322e4b520293cae"
+dependencies = [
+ "proc-macro2",
+]
+
+[[package]]
+name = "rand"
+version = "0.8.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404"
+dependencies = [
+ "libc",
+ "rand_chacha",
+ "rand_core",
+]
+
+[[package]]
+name = "rand_chacha"
+version = "0.3.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88"
+dependencies = [
+ "ppv-lite86",
+ "rand_core",
+]
+
+[[package]]
+name = "rand_core"
+version = "0.6.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c"
+dependencies = [
+ "getrandom",
+]
+
+[[package]]
+name = "serde"
+version = "1.0.188"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "cf9e0fcba69a370eed61bcf2b728575f726b50b55cba78064753d708ddc7549e"
+dependencies = [
+ "serde_derive",
+]
+
+[[package]]
+name = "serde-generate"
+version = "0.25.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f8c9331265d81c61212dc75df7b0836544ed8e32dba77a522f113805ff9a948e"
+dependencies = [
+ "heck",
+ "include_dir",
+ "phf",
+ "serde",
+ "serde-reflection",
+ "textwrap",
+]
+
+[[package]]
+name = "serde-reflection"
+version = "0.3.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f05a5f801ac62a51a49d378fdb3884480041b99aced450b28990673e8ff99895"
+dependencies = [
+ "once_cell",
+ "serde",
+ "thiserror",
+]
+
+[[package]]
+name = "serde_derive"
+version = "1.0.188"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4eca7ac642d82aa35b60049a6eccb4be6be75e599bd2e9adb5f875a737654af2"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn 2.0.29",
+]
+
+[[package]]
+name = "siphasher"
+version = "0.3.11"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "38b58827f4464d87d377d175e90bf58eb00fd8716ff0a62f80356b5e61555d0d"
+
+[[package]]
+name = "smawk"
+version = "0.3.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f67ad224767faa3c7d8b6d91985b78e70a1324408abcb1cfcc2be4c06bc06043"
+
+[[package]]
+name = "syn"
+version = "1.0.109"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "unicode-ident",
+]
+
+[[package]]
+name = "syn"
+version = "2.0.29"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c324c494eba9d92503e6f1ef2e6df781e78f6a7705a0202d9801b198807d518a"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "unicode-ident",
+]
+
+[[package]]
+name = "textwrap"
+version = "0.13.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "cd05616119e612a8041ef58f2b578906cc2531a6069047ae092cfb86a325d835"
+dependencies = [
+ "smawk",
+ "unicode-width",
+]
+
+[[package]]
+name = "thiserror"
+version = "1.0.47"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "97a802ec30afc17eee47b2855fc72e0c4cd62be9b4efe6591edde0ec5bd68d8f"
+dependencies = [
+ "thiserror-impl",
+]
+
+[[package]]
+name = "thiserror-impl"
+version = "1.0.47"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6bb623b56e39ab7dcd4b1b98bb6c8f8d907ed255b18de254088016b27a8ee19b"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn 2.0.29",
+]
+
+[[package]]
+name = "unicode-ident"
+version = "1.0.11"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "301abaae475aa91687eb82514b328ab47a211a533026cb25fc3e519b86adfc3c"
+
+[[package]]
+name = "unicode-segmentation"
+version = "1.10.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1dd624098567895118886609431a7c3b8f516e41d30e0643f03d94592a147e36"
+
+[[package]]
+name = "unicode-width"
+version = "0.1.10"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c0edd1e5b14653f783770bce4a4dabb4a5108a5370a5f5d8cfe8710c361f6c8b"
+
+[[package]]
+name = "wasi"
+version = "0.11.0+wasi-snapshot-preview1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423"
+
+[[package]]
+name = "widestring"
+version = "1.0.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "653f141f39ec16bba3c5abe400a0c60da7468261cc2cbf36805022876bc721a8"
+
+[[package]]
+name = "winapi"
+version = "0.3.7"
+source = "git+https://github.com/Trantect/winapi-rs.git?branch=feature/km#981da7663da0dd9f82bcfceacdc949293a612ef0"
+dependencies = [
+ "winapi-i686-pc-windows-gnu",
+ "winapi-x86_64-pc-windows-gnu",
+]
+
+[[package]]
+name = "winapi-i686-pc-windows-gnu"
+version = "0.4.0"
+source = "git+https://github.com/Trantect/winapi-rs.git?branch=feature/km#981da7663da0dd9f82bcfceacdc949293a612ef0"
+
+[[package]]
+name = "winapi-x86_64-pc-windows-gnu"
+version = "0.4.0"
+source = "git+https://github.com/Trantect/winapi-rs.git?branch=feature/km#981da7663da0dd9f82bcfceacdc949293a612ef0"
+
+[[package]]
+name = "windows-sys"
+version = "0.48.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9"
+dependencies = [
+ "windows-targets",
+]
+
+[[package]]
+name = "windows-targets"
+version = "0.48.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d1eeca1c172a285ee6c2c84c341ccea837e7c01b12fbb2d0fe3c9e550ce49ec8"
+dependencies = [
+ "windows_aarch64_gnullvm",
+ "windows_aarch64_msvc",
+ "windows_i686_gnu",
+ "windows_i686_msvc",
+ "windows_x86_64_gnu",
+ "windows_x86_64_gnullvm",
+ "windows_x86_64_msvc",
+]
+
+[[package]]
+name = "windows_aarch64_gnullvm"
+version = "0.48.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b10d0c968ba7f6166195e13d593af609ec2e3d24f916f081690695cf5eaffb2f"
+
+[[package]]
+name = "windows_aarch64_msvc"
+version = "0.48.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "571d8d4e62f26d4932099a9efe89660e8bd5087775a2ab5cdd8b747b811f1058"
+
+[[package]]
+name = "windows_i686_gnu"
+version = "0.48.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2229ad223e178db5fbbc8bd8d3835e51e566b8474bfca58d2e6150c48bb723cd"
+
+[[package]]
+name = "windows_i686_msvc"
+version = "0.48.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "600956e2d840c194eedfc5d18f8242bc2e17c7775b6684488af3a9fff6fe3287"
+
+[[package]]
+name = "windows_x86_64_gnu"
+version = "0.48.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ea99ff3f8b49fb7a8e0d305e5aec485bd068c2ba691b6e277d29eaeac945868a"
+
+[[package]]
+name = "windows_x86_64_gnullvm"
+version = "0.48.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8f1a05a1ece9a7a0d5a7ccf30ba2c33e3a61a30e042ffd247567d1de1d94120d"
+
+[[package]]
+name = "windows_x86_64_msvc"
+version = "0.48.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d419259aba16b663966e29e6d7c6ecfa0bb8425818bb96f6f1f3c3eb71a6e7b9"
diff --git a/windows_kext/PacketFlow.md b/windows_kext/PacketFlow.md
new file mode 100644
index 000000000..67eab5f31
--- /dev/null
+++ b/windows_kext/PacketFlow.md
@@ -0,0 +1,89 @@
+# There and back again, a packets tale.
+
+An explanation on the complete path of the packet from entering to the exit of the kernel extension.
+
+## Entry
+
+The packet entry point depends on the packet and the internal windows filter state:
+
+- First packet of outbound connection -> AleAuthConnect Layer
+- First packet of inbound connection -> InboundIppacket Layer
+
+## ALE layer
+
+Each defined ALE layer has a filter linked to it. This filter has a state.
+When a decision is made to block or permit a connection it will be saved to the filter state.
+The only way to update the decision in a filter is to clear the whole state and apply the decision for the next packet of each connection.
+
+### First packet
+
+For outgoing connections this logic fallows:
+ - Packet enters in one of the ALE layer
+ - Packet is TCP or UDP
+ 1. Save and absorb packet.
+ 2. Send an event to Portmaster.
+ 2. Create a cache entry.
+ - If Packet is not TCP/UDP forward to packet layer
+
+For incoming connection this logic fallow:
+ - Packet enter in one of the Packet layer, if packet is TCP or UDP it will be forwarded to ALE layer. From there:
+ 1. Save packet and absorb.
+ 2. Send an event to Portmaster.
+ 2. Create a cache entry.
+ 3. Wait for Portmasters decision.
+ - If Packet is not TCP/UDP. It will be handled only by the packet layer.
+
+
+If more packets arrive before Portmaster returns a decision, packet will be absorbed and another event will be sent.
+For Outgoing connection this will happen in ALE layer.
+For Incoming connection this will happen in Packet layer.
+
+### Pormtaster returns a verdict for the connection
+
+Connection cache will be updated and the packet will be injected.
+The next steps depend of the direction of the packet and the verdict
+
+* Permanent Verdict / Outgoing connection
+ - Allow / Block / Drop directly in the ALE layer. For Block and Drop packet layer will not see the rest of the packet in the connection.
+* Temporary Verdict / Outgoing connection
+ - Always Allow - this connections are solely handled by the packet layer. (This is true only for outgoing connections)
+
+* Permanent or Temporary Verdict / Incoming connection
+ - Allow / Block / Drop directly in the ALE layer. They always go through the packet layer first no need to do anything special
+
+Fallowing specifics apply to the ALE layer:
+1. Connections with flag `reauthorize == false` are special. When the flag is `false` that means that a applications is calling a function `connect()` or `accept()` for a connection. This is a special case because we control the result of the function, telling the application that it's allowed or not allowed to continue with the connection. Since we are making request to Portmaster we need to take longer time. This is done with pending the packet. This allows the kernel extension to pause the event and continue when it has the verdict. See `ale_callouts.rs -> save_packet()` function.
+2. If packet payload is present it is from the transport layer.
+
+
+## Packet layer
+
+The logic for the packet is split in two:
+
+### TCP or UDP protocols
+
+The packet layer will not process packets that miss a cache entry:
+- Incoming packet: it will forward it to the ALE layer.
+- Outgoing packet: this is treated as invalid state since ALE should be the entry for the packets. If it happens the packet layer will create a request to Portmaster for it.
+
+For packets with a cache entry:
+- Permanent Verdict: apply the verdict.
+- Redirect Verdict: copy the packet, modify and inject. Drop the original packet.
+- Temporary verdict: send request to Portmaster.
+
+After portmaster returns the verdict for the packet. If its allowed it will be modified (if needed) and injected everything else will be dropped.
+The packet layer will permit all injected packets.
+
+### Not TCP or UDP protocols -> ICMP, IGMP ...
+
+Does packets are treated as with temporary verdict. There will be no cache entry for them.
+Every packet will be send to Portmaster for a decision and re-injected if allowed.
+
+## Connection Cache
+
+It holds information for all TCP and UDP connections. Local and destination ip addresses and ports, verdict, protocol, process id
+It also holds last active time and end time.
+
+Cache entry is removed automatically 1 minute after an end state has been set or after 10 minutes of inactivity.
+
+End stat is set by Endpoint layers or Resource release layers.
\ No newline at end of file
diff --git a/windows_kext/PortmasterKext64.inf b/windows_kext/PortmasterKext64.inf
new file mode 100644
index 000000000..1e3c5aae1
--- /dev/null
+++ b/windows_kext/PortmasterKext64.inf
@@ -0,0 +1,67 @@
+;/*++
+;
+;Copyright (c) Safing ICS Technologies GmbH.
+;
+; This program is free software: you can redistribute it and/or modify
+; it under the terms of the GNU General Public License as published by
+; the Free Software Foundation, either version 3 of the License, or
+; (at your option) any later version.
+;
+; This program is distributed in the hope that it will be useful,
+; but WITHOUT ANY WARRANTY; without even the implied warranty of
+; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+; GNU General Public License for more details.
+;
+; You should have received a copy of the GNU General Public License
+; along with this program. If not, see .
+;
+;--*/
+
+[Version]
+Signature = "$Windows NT$"
+Class = WFPCALLOUTS
+ClassGuid = {57465043-616C-6C6F-7574-5F636C617373}
+Provider = %Provider%
+CatalogFile = PortmasterKext64.Cat
+DriverVer = 01/01/2019,1.0.11.0
+
+[SourceDisksNames]
+1 = %DiskName%
+
+[SourceDisksFiles]
+PortmasterKext64.sys = 1
+
+[DestinationDirs]
+DefaultDestDir = 12 ; %windir%\system32\drivers
+PortmasterKext.DriverFiles = 12 ; %windir%\system32\drivers
+
+[DefaultInstall]
+OptionDesc = %Description%
+CopyFiles = PortmasterKext.DriverFiles
+
+[DefaultInstall.Services]
+AddService = %ServiceName%,,PortmasterKext.Service
+
+[DefaultUninstall]
+DelFiles = PortmasterKext.DriverFiles
+
+[DefaultUninstall.Services]
+DelService = PortmasterKext,0x200 ; SPSVCINST_STOPSERVICE
+
+[PortmasterKext.DriverFiles]
+PortmasterKext64.sys,,,0x00000040 ; COPYFLG_OVERWRITE_OLDER_ONLY
+
+[PortmasterKext.Service]
+DisplayName = %ServiceName%
+Description = %ServiceDesc%
+ServiceType = 1 ; SERVICE_KERNEL_DRIVER
+StartType = 0 ; SERVICE_BOOT_START
+ErrorControl = 1 ; SERVICE_ERROR_NORMAL
+ServiceBinary = %12%\PortmasterKext64.sys
+
+[Strings]
+Provider = "Safing ICS Technologies GmbH"
+DiskName = "PortmasterKext Installation Disk"
+Description = "PortmasterKext Driver"
+ServiceName = "PortmasterKext"
+ServiceDesc = "PortmasterKext Driver"
diff --git a/windows_kext/README.md b/windows_kext/README.md
new file mode 100644
index 000000000..f77de3479
--- /dev/null
+++ b/windows_kext/README.md
@@ -0,0 +1,71 @@
+# Portmaster Windows kext
+Implementation of Safing's Portmaster Windows kernel extension in Rust.
+
+### Documentation
+
+- [Driver](driver/README.md) -> entry point.
+- [WDK](wdk/README.md) -> Windows Driver Kit interface.
+- [Packet Path](PacketDoc.md) -> Detiled documentation of what happens to a packet when it enters the kernel extension.
+- [Release](release/README.md) -> Guide how to do a release build
+
+### Building
+
+The Windows Portmaster Kernel Extension is currently only developed and tested for the amd64 (64-bit) architecture.
+
+__Prerequesites:__
+
+- Visual Studio 2022
+ - Install C++ and Windows 11 SDK (22H2) components
+ - Add `link.exe` and `signtool` in the PATH
+- Rust
+ - https://www.rust-lang.org/tools/install
+- Cargo make(optional)
+ - https://github.com/sagiegurari/cargo-make
+
+__Setup Test Signing:__
+
+In order to test the driver on your machine, you will have to test sign it (starting with Windows 10).
+
+
+Create a new certificate for test signing:
+
+ :: Open a *x64 Free Build Environment* console as Administrator.
+
+ :: Run the MakeCert.exe tool to create a test certificate:
+ MakeCert -r -pe -ss PrivateCertStore -n "CN=DriverCertificate" DriverCertificate.cer
+
+ :: Install the test certificate with CertMgr.exe:
+ CertMgr /add DriverCertificate.cer /s /r localMachine root
+
+
+Enable Test Signing on the dev machine:
+
+ :: Before you can load test-signed drivers, you must enable Windows test mode. To do this, run this command:
+ Bcdedit.exe -set TESTSIGNING ON
+ :: Then, restart Windows. For more information, see The TESTSIGNING Boot Configuration Option.
+
+
+__Build driver:__
+
+```
+cd driver
+cargo build
+```
+> Build also works on linux
+
+__Link and sign:__
+On a windows machine copy `driver.lib` form the project target directory (`driver/target/x86_64-pc-windows-msvc/debug/driver.lib`) in the same folder as `link.bat`.
+Run `link.bat`.
+
+`driver.sys` should appear in the folder. Load and use the driver.
+
+### Test
+- Install go
+ - https://go.dev/dl/
+
+```
+cd kext_tester
+go run .
+```
+
+> make sure the hardcoded path in main.go is pointing to the correct `.sys` file
diff --git a/windows_kext/c_helper/ARM64/c_helper.lib b/windows_kext/c_helper/ARM64/c_helper.lib
new file mode 100644
index 000000000..c9b45ef16
Binary files /dev/null and b/windows_kext/c_helper/ARM64/c_helper.lib differ
diff --git a/windows_kext/c_helper/c_helper.filters b/windows_kext/c_helper/c_helper.filters
new file mode 100644
index 000000000..29fc7ed4e
--- /dev/null
+++ b/windows_kext/c_helper/c_helper.filters
@@ -0,0 +1,22 @@
+
+
+
+
+ {4FC737F1-C7A5-4376-A066-2A32D752A2FF}
+ cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx
+
+
+ {93995380-89BD-4b04-88EB-625FBE52EBFB}
+ h;hpp;hxx;hm;inl;inc;xsd
+
+
+ {67DA6AB6-F800-4c08-8B7A-83BB121AAD01}
+ rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms
+
+
+
+
+ Source Files
+
+
+
\ No newline at end of file
diff --git a/windows_kext/c_helper/c_helper.sln b/windows_kext/c_helper/c_helper.sln
new file mode 100644
index 000000000..134d688a7
--- /dev/null
+++ b/windows_kext/c_helper/c_helper.sln
@@ -0,0 +1,51 @@
+
+Microsoft Visual Studio Solution File, Format Version 12.00
+# Visual Studio Version 17
+VisualStudioVersion = 17.5.33502.453
+MinimumVisualStudioVersion = 10.0.40219.1
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "c_helper", "c_helper.vcxproj", "{39A5E911-A716-4708-8B88-3895183C6372}"
+EndProject
+Global
+ GlobalSection(SolutionConfigurationPlatforms) = preSolution
+ Debug|ARM = Debug|ARM
+ Debug|ARM64 = Debug|ARM64
+ Debug|x64 = Debug|x64
+ Debug|x86 = Debug|x86
+ Release|ARM = Release|ARM
+ Release|ARM64 = Release|ARM64
+ Release|x64 = Release|x64
+ Release|x86 = Release|x86
+ EndGlobalSection
+ GlobalSection(ProjectConfigurationPlatforms) = postSolution
+ {39A5E911-A716-4708-8B88-3895183C6372}.Debug|ARM.ActiveCfg = Debug|ARM
+ {39A5E911-A716-4708-8B88-3895183C6372}.Debug|ARM.Build.0 = Debug|ARM
+ {39A5E911-A716-4708-8B88-3895183C6372}.Debug|ARM.Deploy.0 = Debug|ARM
+ {39A5E911-A716-4708-8B88-3895183C6372}.Debug|ARM64.ActiveCfg = Debug|ARM64
+ {39A5E911-A716-4708-8B88-3895183C6372}.Debug|ARM64.Build.0 = Debug|ARM64
+ {39A5E911-A716-4708-8B88-3895183C6372}.Debug|ARM64.Deploy.0 = Debug|ARM64
+ {39A5E911-A716-4708-8B88-3895183C6372}.Debug|x64.ActiveCfg = Debug|x64
+ {39A5E911-A716-4708-8B88-3895183C6372}.Debug|x64.Build.0 = Debug|x64
+ {39A5E911-A716-4708-8B88-3895183C6372}.Debug|x64.Deploy.0 = Debug|x64
+ {39A5E911-A716-4708-8B88-3895183C6372}.Debug|x86.ActiveCfg = Debug|Win32
+ {39A5E911-A716-4708-8B88-3895183C6372}.Debug|x86.Build.0 = Debug|Win32
+ {39A5E911-A716-4708-8B88-3895183C6372}.Debug|x86.Deploy.0 = Debug|Win32
+ {39A5E911-A716-4708-8B88-3895183C6372}.Release|ARM.ActiveCfg = Release|ARM
+ {39A5E911-A716-4708-8B88-3895183C6372}.Release|ARM.Build.0 = Release|ARM
+ {39A5E911-A716-4708-8B88-3895183C6372}.Release|ARM.Deploy.0 = Release|ARM
+ {39A5E911-A716-4708-8B88-3895183C6372}.Release|ARM64.ActiveCfg = Release|ARM64
+ {39A5E911-A716-4708-8B88-3895183C6372}.Release|ARM64.Build.0 = Release|ARM64
+ {39A5E911-A716-4708-8B88-3895183C6372}.Release|ARM64.Deploy.0 = Release|ARM64
+ {39A5E911-A716-4708-8B88-3895183C6372}.Release|x64.ActiveCfg = Release|x64
+ {39A5E911-A716-4708-8B88-3895183C6372}.Release|x64.Build.0 = Release|x64
+ {39A5E911-A716-4708-8B88-3895183C6372}.Release|x64.Deploy.0 = Release|x64
+ {39A5E911-A716-4708-8B88-3895183C6372}.Release|x86.ActiveCfg = Release|Win32
+ {39A5E911-A716-4708-8B88-3895183C6372}.Release|x86.Build.0 = Release|Win32
+ {39A5E911-A716-4708-8B88-3895183C6372}.Release|x86.Deploy.0 = Release|Win32
+ EndGlobalSection
+ GlobalSection(SolutionProperties) = preSolution
+ HideSolutionNode = FALSE
+ EndGlobalSection
+ GlobalSection(ExtensibilityGlobals) = postSolution
+ SolutionGuid = {91E52350-EBB9-4B0F-9C28-61C0BBAEDC6A}
+ EndGlobalSection
+EndGlobal
diff --git a/windows_kext/c_helper/c_helper.vcxproj b/windows_kext/c_helper/c_helper.vcxproj
new file mode 100644
index 000000000..250c31093
--- /dev/null
+++ b/windows_kext/c_helper/c_helper.vcxproj
@@ -0,0 +1,188 @@
+
+
+
+
+ Debug
+ Win32
+
+
+ Release
+ Win32
+
+
+ Debug
+ x64
+
+
+ Release
+ x64
+
+
+ Debug
+ ARM
+
+
+ Release
+ ARM
+
+
+ Debug
+ ARM64
+
+
+ Release
+ ARM64
+
+
+
+
+
+
+ {39A5E911-A716-4708-8B88-3895183C6372}
+ {0a049372-4c4d-4ea0-a64e-dc6ad88ceca1}
+ v4.5
+ 12.0
+ Debug
+ Win32
+ c_helper
+ KMDF
+ $(LatestTargetPlatformVersion)
+ c_helper
+
+
+
+ Windows10
+ true
+ WindowsKernelModeDriver10.0
+ StaticLibrary
+ Universal
+ Unicode
+
+
+ Windows10
+ false
+ WindowsKernelModeDriver10.0
+ StaticLibrary
+ Universal
+ Unicode
+
+
+ Windows10
+ true
+ WindowsKernelModeDriver10.0
+ StaticLibrary
+ Universal
+ Unicode
+ false
+
+
+ Windows10
+ false
+ WindowsKernelModeDriver10.0
+ StaticLibrary
+ Universal
+ Unicode
+ false
+
+
+ Windows10
+ true
+ WindowsKernelModeDriver10.0
+ StaticLibrary
+ Unicode
+
+
+ Windows10
+ false
+ WindowsKernelModeDriver10.0
+ StaticLibrary
+ Universal
+ Unicode
+
+
+ Windows10
+ true
+ WindowsKernelModeDriver10.0
+ StaticLibrary
+ Universal
+ Unicode
+
+
+ Windows10
+ false
+ WindowsKernelModeDriver10.0
+ StaticLibrary
+ Universal
+ Unicode
+ false
+
+
+
+
+
+
+
+
+
+ $(SolutionDir)$(Platform)
+
+
+ $(SolutionDir)$(Platform)
+
+
+ $(SolutionDir)$(Platform)
+ $(Platform)\$(ConfigurationName)\
+ $(TargetName.Replace(' ',''))
+
+
+
+ _DEBUG;WINAPI_FAMILY=WINAPI_FAMILY_DESKTOP_APP;WINAPI_PARTITION_DESKTOP=1;WINAPI_PARTITION_SYSTEM=1;WINAPI_PARTITION_APP=1;WINAPI_PARTITION_PC_APP=1;%(PreprocessorDefinitions)
+ MultiThreadedDebugDLL
+
+
+
+
+ WINAPI_FAMILY=WINAPI_FAMILY_DESKTOP_APP;WINAPI_PARTITION_DESKTOP=1;WINAPI_PARTITION_SYSTEM=1;WINAPI_PARTITION_APP=1;WINAPI_PARTITION_PC_APP=1;%(PreprocessorDefinitions)
+
+
+
+
+ _DEBUG;WINAPI_FAMILY=WINAPI_FAMILY_DESKTOP_APP;WINAPI_PARTITION_DESKTOP=1;WINAPI_PARTITION_SYSTEM=1;WINAPI_PARTITION_APP=1;WINAPI_PARTITION_PC_APP=1;%(PreprocessorDefinitions)
+ MultiThreadedDebugDLL
+ false
+
+
+
+
+ WINAPI_FAMILY=WINAPI_FAMILY_DESKTOP_APP;WINAPI_PARTITION_DESKTOP=1;WINAPI_PARTITION_SYSTEM=1;WINAPI_PARTITION_APP=1;WINAPI_PARTITION_PC_APP=1;%(PreprocessorDefinitions)
+ false
+
+
+
+
+ _DEBUG;WINAPI_FAMILY=WINAPI_FAMILY_DESKTOP_APP;WINAPI_PARTITION_DESKTOP=1;WINAPI_PARTITION_SYSTEM=1;WINAPI_PARTITION_APP=1;WINAPI_PARTITION_PC_APP=1;%(PreprocessorDefinitions)
+ MultiThreadedDebugDLL
+
+
+
+
+ WINAPI_FAMILY=WINAPI_FAMILY_DESKTOP_APP;WINAPI_PARTITION_DESKTOP=1;WINAPI_PARTITION_SYSTEM=1;WINAPI_PARTITION_APP=1;WINAPI_PARTITION_PC_APP=1;%(PreprocessorDefinitions)
+
+
+
+
+ _DEBUG;WINAPI_FAMILY=WINAPI_FAMILY_DESKTOP_APP;WINAPI_PARTITION_DESKTOP=1;WINAPI_PARTITION_SYSTEM=1;WINAPI_PARTITION_APP=1;WINAPI_PARTITION_PC_APP=1;%(PreprocessorDefinitions)
+ MultiThreadedDebugDLL
+
+
+
+
+ WINAPI_FAMILY=WINAPI_FAMILY_DESKTOP_APP;WINAPI_PARTITION_DESKTOP=1;WINAPI_PARTITION_SYSTEM=1;WINAPI_PARTITION_APP=1;WINAPI_PARTITION_PC_APP=1;%(PreprocessorDefinitions)
+ Default
+ Default
+
+
+
+
+
+
\ No newline at end of file
diff --git a/windows_kext/c_helper/helper.c b/windows_kext/c_helper/helper.c
new file mode 100644
index 000000000..93e893e2a
--- /dev/null
+++ b/windows_kext/c_helper/helper.c
@@ -0,0 +1,89 @@
+
+/*
+ * Name: helper.c
+ */
+
+#include
+#include
+
+#define NDIS640 1 // Windows 8 and Windows Server 2012
+
+#include "Ntifs.h"
+#include // Windows Driver Development Kit
+#include // Windows Driver Foundation
+
+#pragma warning(push)
+#pragma warning(disable: 4201) // Disable "Nameless struct/union" compiler warning for fwpsk.h only!
+#include // Functions and enumerated types used to implement callouts in kernel mode
+#pragma warning(pop) // Re-enable "Nameless struct/union" compiler warning
+
+#include // Functions used for managing IKE and AuthIP main mode (MM) policy and security associations
+#include // Mappings of OS specific function versions (i.e. fn's that end in 0 or 1)
+#include // Used to define GUID's
+#include // Used to define GUID's
+#include "devguid.h"
+#include
+#include
+#include
+
+EVT_WDF_DRIVER_UNLOAD emptyEventUnload;
+
+NTSTATUS pm_InitDriverObject(DRIVER_OBJECT * driverObject, UNICODE_STRING * registryPath, WDFDRIVER * driver, WDFDEVICE * device, wchar_t *win_device_name, wchar_t *dos_device_name, WDF_OBJECT_ATTRIBUTES * objectAttributes, void (*wdfEventUnload)(WDFDRIVER)) {
+ UNICODE_STRING deviceName = { 0 };
+ RtlInitUnicodeString(&deviceName, win_device_name);
+
+ UNICODE_STRING deviceSymlink = { 0 };
+ RtlInitUnicodeString(&deviceSymlink, dos_device_name);
+
+ // Create a WDFDRIVER for this driver
+ WDF_DRIVER_CONFIG config = { 0 };
+ WDF_DRIVER_CONFIG_INIT(&config, WDF_NO_EVENT_CALLBACK);
+ config.DriverInitFlags = WdfDriverInitNonPnpDriver;
+ config.EvtDriverUnload = wdfEventUnload; // <-- Necessary for this driver to unload correctly
+ NTSTATUS status = WdfDriverCreate(driverObject, registryPath, WDF_NO_OBJECT_ATTRIBUTES, &config, driver);
+ if (!NT_SUCCESS(status)) {
+ return status;
+ }
+
+ // Create a WDFDEVICE for this driver
+ PWDFDEVICE_INIT deviceInit = WdfControlDeviceInitAllocate(*driver, &SDDL_DEVOBJ_SYS_ALL_ADM_ALL); // only admins and kernel can access device
+ if (!deviceInit) {
+ return STATUS_INSUFFICIENT_RESOURCES;
+ }
+
+ // Configure the WDFDEVICE_INIT with a name to allow for access from user mode
+ WdfDeviceInitSetDeviceType(deviceInit, FILE_DEVICE_NETWORK);
+ WdfDeviceInitSetCharacteristics(deviceInit, FILE_DEVICE_SECURE_OPEN, false);
+ (void) WdfDeviceInitAssignName(deviceInit, &deviceName);
+ (void) WdfPdoInitAssignRawDevice(deviceInit, &GUID_DEVCLASS_NET);
+ WdfDeviceInitSetDeviceClass(deviceInit, &GUID_DEVCLASS_NET);
+
+ status = WdfDeviceCreate(&deviceInit, objectAttributes, device);
+ if (!NT_SUCCESS(status)) {
+ WdfDeviceInitFree(deviceInit);
+ return status;
+ }
+ status = WdfDeviceCreateSymbolicLink(*device, &deviceSymlink);
+ if (!NT_SUCCESS(status)) {
+ return status;
+ }
+
+ // The system will not send I/O requests or Windows Management Instrumentation (WMI) requests to a control device object unless the driver has called WdfControlFinishInitializing.
+ WdfControlFinishInitializing(*device);
+
+ return STATUS_SUCCESS;
+}
+
+void* pm_WdfObjectGetTypedContextWorker(WDFOBJECT wdfObject, PCWDF_OBJECT_CONTEXT_TYPE_INFO typeInfo) {
+ return WdfObjectGetTypedContextWorker(wdfObject, typeInfo->UniqueType);
+}
+
+DEVICE_OBJECT* pm_GetDeviceObject(WDFDEVICE device) {
+ return WdfDeviceWdmGetDeviceObject(device);
+}
+
+UINT64 pm_QuerySystemTime() {
+ UINT64 timestamp = 0;
+ KeQuerySystemTime(×tamp);
+ return timestamp;
+}
\ No newline at end of file
diff --git a/windows_kext/c_helper/x64/c_helper.lib b/windows_kext/c_helper/x64/c_helper.lib
new file mode 100644
index 000000000..249648f77
Binary files /dev/null and b/windows_kext/c_helper/x64/c_helper.lib differ
diff --git a/windows_kext/driver/.cargo/config.toml b/windows_kext/driver/.cargo/config.toml
new file mode 100644
index 000000000..0a58c1483
--- /dev/null
+++ b/windows_kext/driver/.cargo/config.toml
@@ -0,0 +1,3 @@
+[build]
+target = "x86_64-pc-windows-msvc"
+rustflags = ["-C", "panic=abort"]
diff --git a/windows_kext/driver/Cargo.lock b/windows_kext/driver/Cargo.lock
new file mode 100644
index 000000000..0374a4c7f
--- /dev/null
+++ b/windows_kext/driver/Cargo.lock
@@ -0,0 +1,415 @@
+# This file is automatically @generated by Cargo.
+# It is not intended for manual editing.
+version = 3
+
+[[package]]
+name = "ahash"
+version = "0.8.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "91429305e9f0a25f6205c5b8e0d2db09e0708a7a6df0f42212bb56c32c8ac97a"
+dependencies = [
+ "cfg-if",
+ "once_cell",
+ "version_check",
+ "zerocopy",
+]
+
+[[package]]
+name = "atomic-polyfill"
+version = "1.0.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8cf2bce30dfe09ef0bfaef228b9d414faaf7e563035494d7fe092dba54b300f4"
+dependencies = [
+ "critical-section",
+]
+
+[[package]]
+name = "autocfg"
+version = "1.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa"
+
+[[package]]
+name = "bitflags"
+version = "1.3.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
+
+[[package]]
+name = "byteorder"
+version = "1.5.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b"
+
+[[package]]
+name = "cfg-if"
+version = "1.0.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
+
+[[package]]
+name = "critical-section"
+version = "1.1.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7059fff8937831a9ae6f0fe4d658ffabf58f2ca96aa9dec1c889f936f705f216"
+
+[[package]]
+name = "driver"
+version = "0.0.0"
+dependencies = [
+ "hashbrown",
+ "num",
+ "num-derive",
+ "num-traits",
+ "protocol",
+ "smoltcp",
+ "wdk",
+ "windows-sys",
+]
+
+[[package]]
+name = "hash32"
+version = "0.2.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b0c35f58762feb77d74ebe43bdbc3210f09be9fe6742234d573bacc26ed92b67"
+dependencies = [
+ "byteorder",
+]
+
+[[package]]
+name = "hashbrown"
+version = "0.14.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "290f1a1d9242c78d09ce40a5e87e7554ee637af1351968159f4952f028f75604"
+dependencies = [
+ "ahash",
+]
+
+[[package]]
+name = "heapless"
+version = "0.7.17"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "cdc6457c0eb62c71aac4bc17216026d8410337c4126773b9c5daba343f17964f"
+dependencies = [
+ "atomic-polyfill",
+ "hash32",
+ "rustc_version",
+ "spin",
+ "stable_deref_trait",
+]
+
+[[package]]
+name = "lock_api"
+version = "0.4.11"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3c168f8615b12bc01f9c17e2eb0cc07dcae1940121185446edc3744920e8ef45"
+dependencies = [
+ "autocfg",
+ "scopeguard",
+]
+
+[[package]]
+name = "managed"
+version = "0.8.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0ca88d725a0a943b096803bd34e73a4437208b6077654cc4ecb2947a5f91618d"
+
+[[package]]
+name = "ntstatus"
+version = "0.1.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "96ea8ea6a9a8cbe8fefe99b632bd45ec4b41b0bf234e4d740c516372922fb180"
+dependencies = [
+ "num_enum",
+]
+
+[[package]]
+name = "num"
+version = "0.4.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b05180d69e3da0e530ba2a1dae5110317e49e3b7f3d41be227dc5f92e49ee7af"
+dependencies = [
+ "num-complex",
+ "num-integer",
+ "num-iter",
+ "num-rational",
+ "num-traits",
+]
+
+[[package]]
+name = "num-complex"
+version = "0.4.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1ba157ca0885411de85d6ca030ba7e2a83a28636056c7c699b07c8b6f7383214"
+dependencies = [
+ "num-traits",
+]
+
+[[package]]
+name = "num-derive"
+version = "0.4.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "cfb77679af88f8b125209d354a202862602672222e7f2313fdd6dc349bad4712"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn",
+]
+
+[[package]]
+name = "num-integer"
+version = "0.1.45"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "225d3389fb3509a24c93f5c29eb6bde2586b98d9f016636dff58d7c6f7569cd9"
+dependencies = [
+ "autocfg",
+ "num-traits",
+]
+
+[[package]]
+name = "num-iter"
+version = "0.1.43"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7d03e6c028c5dc5cac6e2dec0efda81fc887605bb3d884578bb6d6bf7514e252"
+dependencies = [
+ "autocfg",
+ "num-integer",
+ "num-traits",
+]
+
+[[package]]
+name = "num-rational"
+version = "0.4.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0638a1c9d0a3c0914158145bc76cff373a75a627e6ecbfb71cbe6f453a5a19b0"
+dependencies = [
+ "autocfg",
+ "num-integer",
+ "num-traits",
+]
+
+[[package]]
+name = "num-traits"
+version = "0.2.17"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "39e3200413f237f41ab11ad6d161bc7239c84dcb631773ccd7de3dfe4b5c267c"
+dependencies = [
+ "autocfg",
+]
+
+[[package]]
+name = "num_enum"
+version = "0.7.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "02339744ee7253741199f897151b38e72257d13802d4ee837285cc2990a90845"
+dependencies = [
+ "num_enum_derive",
+]
+
+[[package]]
+name = "num_enum_derive"
+version = "0.7.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "681030a937600a36906c185595136d26abfebb4aa9c65701cefcaf8578bb982b"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn",
+]
+
+[[package]]
+name = "once_cell"
+version = "1.19.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92"
+
+[[package]]
+name = "proc-macro2"
+version = "1.0.78"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e2422ad645d89c99f8f3e6b88a9fdeca7fabeac836b1002371c4367c8f984aae"
+dependencies = [
+ "unicode-ident",
+]
+
+[[package]]
+name = "protocol"
+version = "0.0.0"
+dependencies = [
+ "num",
+ "num-derive",
+ "num-traits",
+]
+
+[[package]]
+name = "quote"
+version = "1.0.35"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "291ec9ab5efd934aaf503a6466c5d5251535d108ee747472c3977cc5acc868ef"
+dependencies = [
+ "proc-macro2",
+]
+
+[[package]]
+name = "rustc_version"
+version = "0.4.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "bfa0f585226d2e68097d4f95d113b15b83a82e819ab25717ec0590d9584ef366"
+dependencies = [
+ "semver",
+]
+
+[[package]]
+name = "scopeguard"
+version = "1.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49"
+
+[[package]]
+name = "semver"
+version = "1.0.21"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b97ed7a9823b74f99c7742f5336af7be5ecd3eeafcb1507d1fa93347b1d589b0"
+
+[[package]]
+name = "smoltcp"
+version = "0.10.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8d2e3a36ac8fea7b94e666dfa3871063d6e0a5c9d5d4fec9a1a6b7b6760f0229"
+dependencies = [
+ "bitflags",
+ "byteorder",
+ "cfg-if",
+ "heapless",
+ "managed",
+]
+
+[[package]]
+name = "spin"
+version = "0.9.8"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6980e8d7511241f8acf4aebddbb1ff938df5eebe98691418c4468d0b72a96a67"
+dependencies = [
+ "lock_api",
+]
+
+[[package]]
+name = "stable_deref_trait"
+version = "1.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3"
+
+[[package]]
+name = "syn"
+version = "2.0.48"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0f3531638e407dfc0814761abb7c00a5b54992b849452a0646b7f65c9f770f3f"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "unicode-ident",
+]
+
+[[package]]
+name = "unicode-ident"
+version = "1.0.12"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b"
+
+[[package]]
+name = "version_check"
+version = "0.9.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f"
+
+[[package]]
+name = "wdk"
+version = "0.0.0"
+dependencies = [
+ "ntstatus",
+ "widestring",
+ "windows-sys",
+]
+
+[[package]]
+name = "widestring"
+version = "1.0.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "653f141f39ec16bba3c5abe400a0c60da7468261cc2cbf36805022876bc721a8"
+
+[[package]]
+name = "windows-sys"
+version = "0.52.0"
+source = "git+https://github.com/microsoft/windows-rs?rev=41ad38d8c42c92fd23fe25ba4dca76c2d861ca06#41ad38d8c42c92fd23fe25ba4dca76c2d861ca06"
+dependencies = [
+ "windows-targets",
+]
+
+[[package]]
+name = "windows-targets"
+version = "0.52.0"
+source = "git+https://github.com/microsoft/windows-rs?rev=41ad38d8c42c92fd23fe25ba4dca76c2d861ca06#41ad38d8c42c92fd23fe25ba4dca76c2d861ca06"
+dependencies = [
+ "windows_aarch64_gnullvm",
+ "windows_aarch64_msvc",
+ "windows_i686_gnu",
+ "windows_i686_msvc",
+ "windows_x86_64_gnu",
+ "windows_x86_64_gnullvm",
+ "windows_x86_64_msvc",
+]
+
+[[package]]
+name = "windows_aarch64_gnullvm"
+version = "0.52.0"
+source = "git+https://github.com/microsoft/windows-rs?rev=41ad38d8c42c92fd23fe25ba4dca76c2d861ca06#41ad38d8c42c92fd23fe25ba4dca76c2d861ca06"
+
+[[package]]
+name = "windows_aarch64_msvc"
+version = "0.52.0"
+source = "git+https://github.com/microsoft/windows-rs?rev=41ad38d8c42c92fd23fe25ba4dca76c2d861ca06#41ad38d8c42c92fd23fe25ba4dca76c2d861ca06"
+
+[[package]]
+name = "windows_i686_gnu"
+version = "0.52.0"
+source = "git+https://github.com/microsoft/windows-rs?rev=41ad38d8c42c92fd23fe25ba4dca76c2d861ca06#41ad38d8c42c92fd23fe25ba4dca76c2d861ca06"
+
+[[package]]
+name = "windows_i686_msvc"
+version = "0.52.0"
+source = "git+https://github.com/microsoft/windows-rs?rev=41ad38d8c42c92fd23fe25ba4dca76c2d861ca06#41ad38d8c42c92fd23fe25ba4dca76c2d861ca06"
+
+[[package]]
+name = "windows_x86_64_gnu"
+version = "0.52.0"
+source = "git+https://github.com/microsoft/windows-rs?rev=41ad38d8c42c92fd23fe25ba4dca76c2d861ca06#41ad38d8c42c92fd23fe25ba4dca76c2d861ca06"
+
+[[package]]
+name = "windows_x86_64_gnullvm"
+version = "0.52.0"
+source = "git+https://github.com/microsoft/windows-rs?rev=41ad38d8c42c92fd23fe25ba4dca76c2d861ca06#41ad38d8c42c92fd23fe25ba4dca76c2d861ca06"
+
+[[package]]
+name = "windows_x86_64_msvc"
+version = "0.52.0"
+source = "git+https://github.com/microsoft/windows-rs?rev=41ad38d8c42c92fd23fe25ba4dca76c2d861ca06#41ad38d8c42c92fd23fe25ba4dca76c2d861ca06"
+
+[[package]]
+name = "zerocopy"
+version = "0.7.28"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7d6f15f7ade05d2a4935e34a457b936c23dc70a05cc1d97133dc99e7a3fe0f0e"
+dependencies = [
+ "zerocopy-derive",
+]
+
+[[package]]
+name = "zerocopy-derive"
+version = "0.7.28"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "dbbad221e3f78500350ecbd7dfa4e63ef945c05f4c61cb7f4d3f84cd0bba649b"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn",
+]
diff --git a/windows_kext/driver/Cargo.toml b/windows_kext/driver/Cargo.toml
new file mode 100644
index 000000000..09ca639dc
--- /dev/null
+++ b/windows_kext/driver/Cargo.toml
@@ -0,0 +1,26 @@
+[package]
+name = "driver"
+version = "0.0.0"
+edition = "2021"
+
+# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
+
+[lib]
+name = "driver"
+path = "src/lib.rs"
+crate-type = ["staticlib"]
+
+[dependencies]
+wdk = { path = "../wdk" }
+protocol = { path = "../protocol" }
+num = { version = "0.4", default-features = false }
+num-derive = { version = "0.4", default-features = false }
+num-traits = { version = "0.2", default-features = false }
+smoltcp = { version = "0.10", default-features = false, features = ["proto-ipv4", "proto-ipv6"] }
+hashbrown = { version = "0.14.3", default-features = false, features = ["ahash"]}
+
+# WARNING: Do not update. The version was choosen for a reason. See wdk/README.md for more detiels.
+[dependencies.windows-sys]
+git = "https://github.com/microsoft/windows-rs"
+rev = "41ad38d8c42c92fd23fe25ba4dca76c2d861ca06"
+features = ["Wdk_Foundation", "Wdk_Storage_FileSystem", "Wdk_System_SystemServices", "Win32_Foundation", "Win32_Security", "Win32_System_IO", "Win32_System_Kernel", "Win32_System_Power", "Win32_System_WindowsProgramming", "Win32_NetworkManagement_IpHelper", "Win32_Networking_WinSock", "Win32_NetworkManagement_WindowsFilteringPlatform"]
diff --git a/windows_kext/driver/Makefile.toml b/windows_kext/driver/Makefile.toml
new file mode 100644
index 000000000..ee662237b
--- /dev/null
+++ b/windows_kext/driver/Makefile.toml
@@ -0,0 +1,18 @@
+[env.development]
+TARGET_PATH = "target/x86_64-pc-windows-msvc/debug"
+
+[env.production]
+TARGET_PATH = "target/x86_64-pc-windows-msvc/release"
+BUILD_FLAGS = "--release"
+
+[tasks.build-driver]
+script = [
+ "cargo build $BUILD_FLAGS",
+]
+
+[tasks.upload]
+dependencies = ["build-driver"]
+script = [
+ "scp $TARGET_PATH/driver.lib windows:'C:/Dev/'",
+]
+
diff --git a/windows_kext/driver/README.md b/windows_kext/driver/README.md
new file mode 100644
index 000000000..63c8d5814
--- /dev/null
+++ b/windows_kext/driver/README.md
@@ -0,0 +1,70 @@
+# Driver
+
+This is the entry point of the Kernel extension.
+
+## Quick overview
+
+`entry.rs`:
+This file contains the entry point and calling all the needed initialization code.
+- Setting up the driver object
+- Allocating global state
+
+`fn driver_entry()` -> entry pointer of the driver.
+
+`device.rs`:
+Holds the global state of the driver.
+Initialization: Setting up global state, Filter engine and callouts.
+
+Portmaster communication:
+The communication happens concurrently with the File read/write API.
+That means when Pormtaster sends a command the kernel extension will start to process it and queue the result in the `IOQueue`.
+
+`fn read()` -> called on read request from Portmaster
+- `IOQueue` holds all the events queued for Portmaster.
+
+Blocks until there is a element that can be poped or shutdown request is sent from Portmaster.
+If there is more then one event in the queue it will write as much as it can in the supplied buffer.
+
+`fn write()` -> called on write request from Portmaster.
+Used when Portmaster wants to send a command to kernel extension.
+Verdict Response, GetLogs ... (see `protocol` for list of all the commands)
+
+
+## Callouts
+
+`callouts.rs` -> defines the list of all used callouts in the kernel extension.
+
+ALE (Application Layer Enforcement)
+https://learn.microsoft.com/en-us/windows/win32/fwp/application-layer-enforcement--ale-
+
+### ALE Auth
+
+Connection level filtering. It will make a decision based on the first packet of a connection. Works together with the packet layer to provide firewall functionality.
+- **AleLayerOutboundV4**
+- **AleLayerInboundV4**
+- **AleLayerOutboundV6**
+- **AleLayerInboundV6**
+
+
+### ALE endpoint / resource assignment and release
+
+Used to listen for event when connection has ended. Does no filtering.
+- **AleEndpointClosureV4, AleEndpointClosureV6** - Triggered when connection to an endpoint has ended. Usually only TCP is triggered. The triggered connection will be marked for deletion.
+
+- **AleResourceAssignmentV4, AleResourceAssignmentV6** -> only for logging (not used)
+- AleResourceReleaseV4, AleResourceReleaseV6 -> Triggered when port is release from an application. The triggered connection/s will be marked for deletion.
+
+### Stream layer
+
+This layer works on the application OSI layer. Meaning that only the payload of the TCP/UDP connection will be available.
+It is used for bandwidth monitoring. This functionality is completely separate from the rest of the system so it can be disabled or enabled without affect anything else.
+
+- **StreamLayerV4, StreamLayerV6** -> For TCP connections
+- **DatagramDataLayerV4, DatagramDataLayerV6** -> For UDP connections
+
+
+### Packet layer
+
+This layer handled each packet on the network OSI layer. Works together with ALE Auth layer to provide firewall functionality.
+- **IPPacketOutboundV4, IPPacketOutboundV6** -> Triggered on every outbound packet.
+- **IPPacketInboundV4, IPPacketInboundV6** -> Triggered on every inbound packet.
diff --git a/windows_kext/driver/rust-toolchain b/windows_kext/driver/rust-toolchain
new file mode 100644
index 000000000..2bf5ad044
--- /dev/null
+++ b/windows_kext/driver/rust-toolchain
@@ -0,0 +1 @@
+stable
diff --git a/windows_kext/driver/src/ale_callouts.rs b/windows_kext/driver/src/ale_callouts.rs
new file mode 100644
index 000000000..c45b7f4ca
--- /dev/null
+++ b/windows_kext/driver/src/ale_callouts.rs
@@ -0,0 +1,537 @@
+use crate::connection::{Connection, ConnectionV4, ConnectionV6, Direction, Verdict};
+use crate::connection_map::Key;
+use crate::device::{Device, Packet};
+
+use crate::info;
+use smoltcp::wire::{
+ IpAddress, IpProtocol, Ipv4Address, Ipv6Address, IPV4_HEADER_LEN, IPV6_HEADER_LEN,
+};
+use wdk::filter_engine::callout_data::CalloutData;
+use wdk::filter_engine::layer::{
+ self, FieldsAleAuthConnectV4, FieldsAleAuthConnectV6, FieldsAleAuthRecvAcceptV4,
+ FieldsAleAuthRecvAcceptV6, ValueType,
+};
+use wdk::filter_engine::net_buffer::NetBufferList;
+use wdk::filter_engine::packet::{Injector, TransportPacketList};
+
+// ALE Layers
+
+#[derive(Debug)]
+#[allow(dead_code)]
+struct AleLayerData {
+ is_ipv6: bool,
+ reauthorize: bool,
+ process_id: u64,
+ protocol: IpProtocol,
+ direction: Direction,
+ local_ip: IpAddress,
+ local_port: u16,
+ remote_ip: IpAddress,
+ remote_port: u16,
+ interface_index: u32,
+ sub_interface_index: u32,
+}
+
+impl AleLayerData {
+ fn as_key(&self) -> Key {
+ let mut local_port = 0;
+ let mut remote_port = 0;
+ match self.protocol {
+ IpProtocol::Tcp | IpProtocol::Udp => {
+ local_port = self.local_port;
+ remote_port = self.remote_port;
+ }
+ _ => {}
+ }
+
+ Key {
+ protocol: self.protocol,
+ local_address: self.local_ip,
+ local_port,
+ remote_address: self.remote_ip,
+ remote_port,
+ }
+ }
+}
+
+fn get_protocol(data: &CalloutData, index: usize) -> IpProtocol {
+ IpProtocol::from(data.get_value_u8(index))
+}
+
+fn get_ipv4_address(data: &CalloutData, index: usize) -> IpAddress {
+ IpAddress::Ipv4(Ipv4Address::from_bytes(
+ &data.get_value_u32(index).to_be_bytes(),
+ ))
+}
+
+fn get_ipv6_address(data: &CalloutData, index: usize) -> IpAddress {
+ IpAddress::Ipv6(Ipv6Address::from_bytes(data.get_value_byte_array16(index)))
+}
+
+pub fn ale_layer_connect_v4(data: CalloutData) {
+ type Fields = FieldsAleAuthConnectV4;
+ let ale_data = AleLayerData {
+ is_ipv6: false,
+ reauthorize: data.is_reauthorize(Fields::Flags as usize),
+ process_id: data.get_process_id().unwrap_or(0),
+ protocol: get_protocol(&data, Fields::IpProtocol as usize),
+ direction: Direction::Outbound,
+ local_ip: get_ipv4_address(&data, Fields::IpLocalAddress as usize),
+ local_port: data.get_value_u16(Fields::IpLocalPort as usize),
+ remote_ip: get_ipv4_address(&data, Fields::IpRemoteAddress as usize),
+ remote_port: data.get_value_u16(Fields::IpRemotePort as usize),
+ interface_index: 0,
+ sub_interface_index: 0,
+ };
+
+ ale_layer_auth(data, ale_data);
+}
+
+pub fn ale_layer_accept_v4(data: CalloutData) {
+ type Fields = FieldsAleAuthRecvAcceptV4;
+ let ale_data = AleLayerData {
+ is_ipv6: false,
+ reauthorize: data.is_reauthorize(Fields::Flags as usize),
+ process_id: data.get_process_id().unwrap_or(0),
+ protocol: get_protocol(&data, Fields::IpProtocol as usize),
+ direction: Direction::Inbound,
+ local_ip: get_ipv4_address(&data, Fields::IpLocalAddress as usize),
+ local_port: data.get_value_u16(Fields::IpLocalPort as usize),
+ remote_ip: get_ipv4_address(&data, Fields::IpRemoteAddress as usize),
+ remote_port: data.get_value_u16(Fields::IpRemotePort as usize),
+ interface_index: data.get_value_u32(Fields::InterfaceIndex as usize),
+ sub_interface_index: data.get_value_u32(Fields::SubInterfaceIndex as usize),
+ };
+ ale_layer_auth(data, ale_data);
+}
+
+pub fn ale_layer_connect_v6(data: CalloutData) {
+ type Fields = FieldsAleAuthConnectV6;
+
+ let ale_data = AleLayerData {
+ is_ipv6: true,
+ reauthorize: data.is_reauthorize(Fields::Flags as usize),
+ process_id: data.get_process_id().unwrap_or(0),
+ protocol: get_protocol(&data, Fields::IpProtocol as usize),
+ direction: Direction::Outbound,
+ local_ip: get_ipv6_address(&data, Fields::IpLocalAddress as usize),
+ local_port: data.get_value_u16(Fields::IpLocalPort as usize),
+ remote_ip: get_ipv6_address(&data, Fields::IpRemoteAddress as usize),
+ remote_port: data.get_value_u16(Fields::IpRemotePort as usize),
+ interface_index: data.get_value_u32(Fields::InterfaceIndex as usize),
+ sub_interface_index: data.get_value_u32(Fields::SubInterfaceIndex as usize),
+ };
+
+ ale_layer_auth(data, ale_data);
+}
+
+pub fn ale_layer_accept_v6(data: CalloutData) {
+ type Fields = FieldsAleAuthRecvAcceptV6;
+ let ale_data = AleLayerData {
+ is_ipv6: true,
+ reauthorize: data.is_reauthorize(Fields::Flags as usize),
+ process_id: data.get_process_id().unwrap_or(0),
+ protocol: get_protocol(&data, Fields::IpProtocol as usize),
+ direction: Direction::Inbound,
+ local_ip: get_ipv6_address(&data, Fields::IpLocalAddress as usize),
+ local_port: data.get_value_u16(Fields::IpLocalPort as usize),
+ remote_ip: get_ipv6_address(&data, Fields::IpRemoteAddress as usize),
+ remote_port: data.get_value_u16(Fields::IpRemotePort as usize),
+ interface_index: data.get_value_u32(Fields::InterfaceIndex as usize),
+ sub_interface_index: data.get_value_u32(Fields::SubInterfaceIndex as usize),
+ };
+ ale_layer_auth(data, ale_data);
+}
+
+fn ale_layer_auth(mut data: CalloutData, ale_data: AleLayerData) {
+ let Some(device) = crate::entry::get_device() else {
+ return;
+ };
+
+ match ale_data.protocol {
+ IpProtocol::Tcp | IpProtocol::Udp => {
+ // Only TCP and UDP make sense to be supported in the ALE layer.
+ // Everything else is not associated with a connection and will be handled in the packet layer.
+ }
+ _ => {
+ // Outbound: Will be handled by packet layer next.
+ // Inbound: Was already handled by the packet layer.
+ data.action_permit();
+ return;
+ }
+ }
+
+ let key = ale_data.as_key();
+
+ // Check if connection is already in cache.
+ let verdict = if ale_data.is_ipv6 {
+ device
+ .connection_cache
+ .read_connection_v6(&key, |conn| -> Option {
+ // Function is behind spin lock, just copy and return.
+ Some(conn.verdict)
+ })
+ } else {
+ device
+ .connection_cache
+ .read_connection_v4(&ale_data.as_key(), |conn| -> Option {
+ // Function is behind spin lock, just copy and return.
+ Some(conn.verdict)
+ })
+ };
+
+ // Connection already in cache.
+ if let Some(verdict) = verdict {
+ crate::dbg!("processing existing connection: {} {}", key, verdict);
+ match verdict {
+ // No verdict yet
+ Verdict::Undecided => {
+ crate::dbg!("saving packet: {}", key);
+ // Connection is already pended. Save packet and wait for verdict.
+ match save_packet(device, &mut data, &ale_data, false) {
+ Ok(packet) => {
+ let info = device.packet_cache.push(
+ (key, packet),
+ ale_data.process_id,
+ ale_data.direction,
+ true,
+ );
+ if let Some(info) = info {
+ let _ = device.event_queue.push(info);
+ }
+ }
+ Err(err) => {
+ crate::err!("failed to pend packet: {}", err);
+ }
+ };
+ data.block_and_absorb();
+ }
+ // There is a verdict
+ Verdict::PermanentAccept
+ | Verdict::Accept
+ | Verdict::RedirectNameServer
+ | Verdict::RedirectTunnel => {
+ // Continue to packet layer.
+ data.action_permit();
+ }
+ Verdict::PermanentBlock | Verdict::Undeterminable | Verdict::Failed => {
+ // Packet layer will not see this connection.
+ crate::dbg!("permanent block {}", key);
+ data.action_block();
+ }
+ Verdict::PermanentDrop => {
+ // Packet layer will not see this connection.
+ crate::dbg!("permanent drop {}", key);
+ data.block_and_absorb();
+ }
+ Verdict::Block => {
+ if let Direction::Outbound = ale_data.direction {
+ // Handled by packet layer.
+ data.action_permit();
+ } else {
+ // packet layer will still see the packets.
+ data.action_block();
+ }
+ }
+ Verdict::Drop => {
+ if let Direction::Outbound = ale_data.direction {
+ // Handled by packet layer.
+ data.action_permit();
+ } else {
+ // packet layer will still see the packets.
+ data.block_and_absorb();
+ }
+ }
+ }
+ } else {
+ crate::dbg!("pending connection: {} {}", key, ale_data.direction);
+ // Only first packet of a connection can be pended: reauthorize == false
+ let can_pend_connection = !ale_data.reauthorize;
+ match save_packet(device, &mut data, &ale_data, can_pend_connection) {
+ Ok(packet) => {
+ let info = device.packet_cache.push(
+ (key, packet),
+ ale_data.process_id,
+ ale_data.direction,
+ true,
+ );
+ if let Some(info) = info {
+ let _ = device.event_queue.push(info);
+ }
+ }
+ Err(err) => {
+ crate::err!("failed to pend packet: {}", err);
+ }
+ };
+
+ // Connection is not in cache, add it.
+ crate::dbg!("adding connection: {} PID: {}", key, ale_data.process_id);
+ if ale_data.is_ipv6 {
+ let conn =
+ ConnectionV6::from_key(&key, ale_data.process_id, ale_data.direction).unwrap();
+ device.connection_cache.add_connection_v6(conn);
+ } else {
+ let conn =
+ ConnectionV4::from_key(&key, ale_data.process_id, ale_data.direction).unwrap();
+ device.connection_cache.add_connection_v4(conn);
+ }
+
+ // Drop packet. It will be re-injected after Portmaster returns a verdict.
+ data.block_and_absorb();
+ }
+}
+
+fn save_packet(
+ device: &Device,
+ callout_data: &mut CalloutData,
+ ale_data: &AleLayerData,
+ pend: bool,
+) -> Result {
+ let mut packet_list = None;
+ let mut save_packet_list = true;
+ match ale_data.protocol {
+ IpProtocol::Tcp => {
+ if let Direction::Outbound = ale_data.direction {
+ // Only time a packet data is missing is during connect state of outbound TCP connection.
+ // Don't save packet list only if connection is outbound, reauthorize is false and the protocol is TCP.
+ save_packet_list = ale_data.reauthorize;
+ }
+ }
+ _ => {}
+ };
+ if save_packet_list {
+ packet_list = create_packet_list(device, callout_data, ale_data);
+ }
+ if pend && matches!(ale_data.protocol, IpProtocol::Tcp | IpProtocol::Udp) {
+ match callout_data.pend_operation(packet_list) {
+ Ok(classify_defer) => Ok(Packet::AleLayer(classify_defer)),
+ Err(err) => Err(alloc::format!("failed to defer connection: {}", err)),
+ }
+ } else {
+ Ok(Packet::AleLayer(callout_data.pend_filter_rest(packet_list)))
+ }
+}
+
+fn create_packet_list(
+ device: &Device,
+ callout_data: &mut CalloutData,
+ ale_data: &AleLayerData,
+) -> Option {
+ let mut nbl = NetBufferList::new(callout_data.get_layer_data() as _);
+ let mut inbound = false;
+ if let Direction::Inbound = ale_data.direction {
+ if ale_data.is_ipv6 {
+ nbl.retreat(IPV6_HEADER_LEN as u32, true);
+ } else {
+ nbl.retreat(IPV4_HEADER_LEN as u32, true);
+ }
+ inbound = true;
+ }
+
+ let address: &[u8] = match &ale_data.remote_ip {
+ IpAddress::Ipv4(address) => &address.0,
+ IpAddress::Ipv6(address) => &address.0,
+ };
+ if let Ok(clone) = nbl.clone(&device.network_allocator) {
+ return Some(Injector::from_ale_callout(
+ ale_data.is_ipv6,
+ callout_data,
+ clone,
+ address,
+ inbound,
+ ale_data.interface_index,
+ ale_data.sub_interface_index,
+ ));
+ }
+ return None;
+}
+
+pub fn endpoint_closure_v4(data: CalloutData) {
+ type Fields = layer::FieldsAleEndpointClosureV4;
+ let Some(device) = crate::entry::get_device() else {
+ return;
+ };
+ let ip_address_type = data.get_value_type(Fields::IpLocalAddress as usize);
+ if let ValueType::FwpUint32 = ip_address_type {
+ let key = Key {
+ protocol: get_protocol(&data, Fields::IpProtocol as usize),
+ local_address: get_ipv4_address(&data, Fields::IpLocalAddress as usize),
+ local_port: data.get_value_u16(Fields::IpLocalPort as usize),
+ remote_address: get_ipv4_address(&data, Fields::IpRemoteAddress as usize),
+ remote_port: data.get_value_u16(Fields::IpRemotePort as usize),
+ };
+
+ let conn = device.connection_cache.end_connection_v4(key);
+ if let Some(conn) = conn {
+ let info = protocol::info::connection_end_event_v4_info(
+ data.get_process_id().unwrap_or(0),
+ conn.get_direction() as u8,
+ u8::from(get_protocol(&data, Fields::IpProtocol as usize)),
+ conn.local_address.0,
+ conn.remote_address.0,
+ conn.local_port,
+ conn.remote_port,
+ );
+ let _ = device.event_queue.push(info);
+ }
+ } else {
+ // Invalid ip address type. Just ignore the error.
+ // err!(
+ // device.logger,
+ // "unknown ipv4 address type: {:?}",
+ // ip_address_type
+ // );
+ }
+}
+
+pub fn endpoint_closure_v6(data: CalloutData) {
+ type Fields = layer::FieldsAleEndpointClosureV6;
+ let Some(device) = crate::entry::get_device() else {
+ return;
+ };
+ let local_ip_address_type = data.get_value_type(Fields::IpLocalAddress as usize);
+ let remote_ip_address_type = data.get_value_type(Fields::IpRemoteAddress as usize);
+
+ if let ValueType::FwpByteArray16Type = local_ip_address_type {
+ if let ValueType::FwpByteArray16Type = remote_ip_address_type {
+ let key = Key {
+ protocol: get_protocol(&data, Fields::IpProtocol as usize),
+ local_address: get_ipv6_address(&data, Fields::IpLocalAddress as usize),
+ local_port: data.get_value_u16(Fields::IpLocalPort as usize),
+ remote_address: get_ipv6_address(&data, Fields::IpRemoteAddress as usize),
+ remote_port: data.get_value_u16(Fields::IpRemotePort as usize),
+ };
+
+ let conn = device.connection_cache.end_connection_v6(key);
+ if let Some(conn) = conn {
+ let info = protocol::info::connection_end_event_v6_info(
+ data.get_process_id().unwrap_or(0),
+ conn.get_direction() as u8,
+ u8::from(get_protocol(&data, Fields::IpProtocol as usize)),
+ conn.local_address.0,
+ conn.remote_address.0,
+ conn.local_port,
+ conn.remote_port,
+ );
+ let _ = device.event_queue.push(info);
+ }
+ }
+ }
+}
+
+pub fn ale_resource_monitor(data: CalloutData) {
+ let Some(device) = crate::entry::get_device() else {
+ return;
+ };
+ match data.layer {
+ layer::Layer::AleResourceAssignmentV4Discard => {
+ type Fields = layer::FieldsAleResourceAssignmentV4;
+ if let Some(conns) = device.connection_cache.end_all_on_port_v4((
+ get_protocol(&data, Fields::IpProtocol as usize),
+ data.get_value_u16(Fields::IpLocalPort as usize),
+ )) {
+ let process_id = data.get_process_id().unwrap_or(0);
+ info!(
+ "Port {}/{} Ipv4 assign request discarded pid={}",
+ data.get_value_u16(Fields::IpLocalPort as usize),
+ get_protocol(&data, Fields::IpProtocol as usize),
+ process_id,
+ );
+ for conn in conns {
+ let info = protocol::info::connection_end_event_v4_info(
+ process_id,
+ conn.get_direction() as u8,
+ data.get_value_u8(Fields::IpProtocol as usize),
+ conn.local_address.0,
+ conn.remote_address.0,
+ conn.local_port,
+ conn.remote_port,
+ );
+ let _ = device.event_queue.push(info);
+ }
+ }
+ }
+ layer::Layer::AleResourceAssignmentV6Discard => {
+ type Fields = layer::FieldsAleResourceAssignmentV6;
+ if let Some(conns) = device.connection_cache.end_all_on_port_v6((
+ get_protocol(&data, Fields::IpProtocol as usize),
+ data.get_value_u16(Fields::IpLocalPort as usize),
+ )) {
+ let process_id = data.get_process_id().unwrap_or(0);
+ info!(
+ "Port {}/{} Ipv6 assign request discarded pid={}",
+ data.get_value_u16(Fields::IpLocalPort as usize),
+ get_protocol(&data, Fields::IpProtocol as usize),
+ process_id,
+ );
+ for conn in conns {
+ let info = protocol::info::connection_end_event_v6_info(
+ process_id,
+ conn.get_direction() as u8,
+ data.get_value_u8(Fields::IpProtocol as usize),
+ conn.local_address.0,
+ conn.remote_address.0,
+ conn.local_port,
+ conn.remote_port,
+ );
+ let _ = device.event_queue.push(info);
+ }
+ }
+ }
+ layer::Layer::AleResourceReleaseV4 => {
+ type Fields = layer::FieldsAleResourceReleaseV4;
+ if let Some(conns) = device.connection_cache.end_all_on_port_v4((
+ get_protocol(&data, Fields::IpProtocol as usize),
+ data.get_value_u16(Fields::IpLocalPort as usize),
+ )) {
+ let process_id = data.get_process_id().unwrap_or(0);
+ info!(
+ "Port {}/{} released pid={}",
+ data.get_value_u16(Fields::IpLocalPort as usize),
+ get_protocol(&data, Fields::IpProtocol as usize),
+ process_id,
+ );
+ for conn in conns {
+ let info = protocol::info::connection_end_event_v4_info(
+ process_id,
+ conn.get_direction() as u8,
+ data.get_value_u8(Fields::IpProtocol as usize),
+ conn.local_address.0,
+ conn.remote_address.0,
+ conn.local_port,
+ conn.remote_port,
+ );
+ let _ = device.event_queue.push(info);
+ }
+ }
+ }
+ layer::Layer::AleResourceReleaseV6 => {
+ type Fields = layer::FieldsAleResourceReleaseV6;
+ if let Some(conns) = device.connection_cache.end_all_on_port_v6((
+ get_protocol(&data, Fields::IpProtocol as usize),
+ data.get_value_u16(Fields::IpLocalPort as usize),
+ )) {
+ let process_id = data.get_process_id().unwrap_or(0);
+ info!(
+ "Port {}/{} released pid={}",
+ data.get_value_u16(Fields::IpLocalPort as usize),
+ get_protocol(&data, Fields::IpProtocol as usize),
+ process_id,
+ );
+ for conn in conns {
+ let info = protocol::info::connection_end_event_v6_info(
+ process_id,
+ conn.get_direction() as u8,
+ data.get_value_u8(Fields::IpProtocol as usize),
+ conn.local_address.0,
+ conn.remote_address.0,
+ conn.local_port,
+ conn.remote_port,
+ );
+ let _ = device.event_queue.push(info);
+ }
+ }
+ }
+ _ => {}
+ }
+}
diff --git a/windows_kext/driver/src/array_holder.rs b/windows_kext/driver/src/array_holder.rs
new file mode 100644
index 000000000..077a24957
--- /dev/null
+++ b/windows_kext/driver/src/array_holder.rs
@@ -0,0 +1,25 @@
+use core::cell::RefCell;
+
+use alloc::vec::Vec;
+
+pub struct ArrayHolder(RefCell