Compare commits

...
Author SHA1 Message Date
Fedor Korotkov c377206064 chore: remove unused skill file for PR fixes 2026-01-08 15:27:44 +01:00
Fedor Korotkov 7623d698ce revert: restore args parsing style
- switch back to let mut args: Args = Args::parse()
2026-01-08 15:08:47 +01:00
Fedor KorotkovandClaude Opus 4.5 1ccb0ee4b4 fix: address PR review feedback
- simplify allow/block resolution without mem::take (refs #2661210452)
- use dns.google test domain with stable A records (refs #2661236654)

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-08 12:34:46 +01:00
Fedor Korotkov 50b5e2af2d Add domain resolution for allow/block entries
- Updated `--allow` and `--block` flags to accept domains, alongside CIDRs and IPs.
- Implemented A-record resolution for domain entries during startup.
- Added tests for domain resolution functionality.
2025-12-20 06:44:51 -05:00
dependabot[bot] c1ddb2afc6 Bump the all-updates group with 3 updates (#132)
Bumps the all-updates group with 3 updates: [libc](https://github.com/rust-lang/libc), [system-configuration](https://github.com/mullvad/system-configuration-rs) and [log](https://github.com/rust-lang/log).


Updates `libc` from 0.2.177 to 0.2.178
- [Release notes](https://github.com/rust-lang/libc/releases)
- [Changelog](https://github.com/rust-lang/libc/blob/0.2.178/CHANGELOG.md)
- [Commits](https://github.com/rust-lang/libc/compare/0.2.177...0.2.178)

Updates `system-configuration` from 0.6.1 to 0.7.0
- [Changelog](https://github.com/mullvad/system-configuration-rs/blob/main/CHANGELOG.md)
- [Commits](https://github.com/mullvad/system-configuration-rs/compare/v0.6.1...v0.7.0)

Updates `log` from 0.4.28 to 0.4.29
- [Release notes](https://github.com/rust-lang/log/releases)
- [Changelog](https://github.com/rust-lang/log/blob/master/CHANGELOG.md)
- [Commits](https://github.com/rust-lang/log/compare/0.4.28...0.4.29)

---
updated-dependencies:
- dependency-name: libc
  dependency-version: 0.2.178
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: all-updates
- dependency-name: system-configuration
  dependency-version: 0.7.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: all-updates
- dependency-name: log
  dependency-version: 0.4.29
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: all-updates
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-12-08 11:55:22 +01:00
dependabot[bot] 401dea6612 Bump the all-updates group with 2 updates (#131)
Bumps the all-updates group with 2 updates: [sentry](https://github.com/getsentry/sentry-rust) and [sentry-anyhow](https://github.com/getsentry/sentry-rust).


Updates `sentry` from 0.45.0 to 0.46.0
- [Release notes](https://github.com/getsentry/sentry-rust/releases)
- [Changelog](https://github.com/getsentry/sentry-rust/blob/master/CHANGELOG.md)
- [Commits](https://github.com/getsentry/sentry-rust/compare/0.45.0...0.46.0)

Updates `sentry-anyhow` from 0.45.0 to 0.46.0
- [Release notes](https://github.com/getsentry/sentry-rust/releases)
- [Changelog](https://github.com/getsentry/sentry-rust/blob/master/CHANGELOG.md)
- [Commits](https://github.com/getsentry/sentry-rust/compare/0.45.0...0.46.0)

---
updated-dependencies:
- dependency-name: sentry
  dependency-version: 0.46.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: all-updates
- dependency-name: sentry-anyhow
  dependency-version: 0.46.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: all-updates
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-12-01 13:37:30 +04:00
dependabot[bot] 1dcb0755df Bump clap from 4.5.51 to 4.5.53 in the all-updates group (#130)
Bumps the all-updates group with 1 update: [clap](https://github.com/clap-rs/clap).


Updates `clap` from 4.5.51 to 4.5.53
- [Release notes](https://github.com/clap-rs/clap/releases)
- [Changelog](https://github.com/clap-rs/clap/blob/master/CHANGELOG.md)
- [Commits](https://github.com/clap-rs/clap/compare/clap_complete-v4.5.51...clap_complete-v4.5.53)

---
updated-dependencies:
- dependency-name: clap
  dependency-version: 4.5.53
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: all-updates
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-11-24 14:53:18 +04:00
dependabot[bot] 1706062004 Bump clap from 4.5.50 to 4.5.51 in the all-updates group (#129)
Bumps the all-updates group with 1 update: [clap](https://github.com/clap-rs/clap).


Updates `clap` from 4.5.50 to 4.5.51
- [Release notes](https://github.com/clap-rs/clap/releases)
- [Changelog](https://github.com/clap-rs/clap/blob/master/CHANGELOG.md)
- [Commits](https://github.com/clap-rs/clap/compare/clap_complete-v4.5.50...clap_complete-v4.5.51)

---
updated-dependencies:
- dependency-name: clap
  dependency-version: 4.5.51
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: all-updates
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-11-03 14:17:54 +04:00
Nikolay Edigaryev dfc04a49e4 .goreleaser.yml: require macOS Sequoia (see #128) 2025-10-28 13:49:09 +01:00
Nikolay Edigaryev 7dc5992b55 Perform batched reads from host to improve efficiency (#128)
* Perform batched reads from host to improve efficiency

* CI: don't test on macOS Sonoma
2025-10-28 16:48:05 +04:00
dependabot[bot] 58d4b32258 Bump the all-updates group with 3 updates (#127)
Bumps the all-updates group with 3 updates: [dhcproto](https://github.com/bluecatengineering/dhcproto), [clap](https://github.com/clap-rs/clap) and [serial_test](https://github.com/palfrey/serial_test).


Updates `dhcproto` from `96a6845` to `b4ea30d`
- [Release notes](https://github.com/bluecatengineering/dhcproto/releases)
- [Commits](https://github.com/bluecatengineering/dhcproto/compare/96a6845f7154a54e9c18d3595807c62e7994d5fe...b4ea30defc01e7ae66e7075d6a0533d9bb9503dc)

Updates `clap` from 4.5.49 to 4.5.50
- [Release notes](https://github.com/clap-rs/clap/releases)
- [Changelog](https://github.com/clap-rs/clap/blob/master/CHANGELOG.md)
- [Commits](https://github.com/clap-rs/clap/compare/clap_complete-v4.5.49...clap_complete-v4.5.50)

Updates `serial_test` from 0.10.0 to 3.2.0
- [Release notes](https://github.com/palfrey/serial_test/releases)
- [Commits](https://github.com/palfrey/serial_test/compare/v0.10.0...v3.2.0)

---
updated-dependencies:
- dependency-name: dhcproto
  dependency-version: b4ea30defc01e7ae66e7075d6a0533d9bb9503dc
  dependency-type: direct:production
  dependency-group: all-updates
- dependency-name: clap
  dependency-version: 4.5.50
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: all-updates
- dependency-name: serial_test
  dependency-version: 3.2.0
  dependency-type: direct:production
  update-type: version-update:semver-major
  dependency-group: all-updates
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-10-27 16:10:36 +04:00
7 changed files with 196 additions and 73 deletions
-3
View File
@@ -15,9 +15,6 @@ task:
task:
alias: Test
matrix:
- name: Test on macOS Sonoma
macos_instance:
image: ghcr.io/cirruslabs/macos-runner:sonoma
- name: Test on macOS Sequoia
macos_instance:
image: ghcr.io/cirruslabs/macos-runner:sequoia
+2
View File
@@ -29,3 +29,5 @@ brews:
homepage: https://github.com/cirruslabs/softnet
description: Software networking with isolation for Tart
skip_upload: auto
custom_block: |
depends_on :macos => :sequoia
Generated
+59 -46
View File
@@ -363,9 +363,9 @@ checksum = "613afe47fcd5fac7ccf1db93babcb082c5994d996f20b8b159f2ad1658eb5724"
[[package]]
name = "clap"
version = "4.5.49"
version = "4.5.53"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f4512b90fa68d3a9932cea5184017c5d200f5921df706d45e853537dea51508f"
checksum = "c9e340e012a1bf4935f5282ed1436d1489548e8f72308207ea5df0e23d2d03f8"
dependencies = [
"clap_builder",
"clap_derive",
@@ -373,9 +373,9 @@ dependencies = [
[[package]]
name = "clap_builder"
version = "4.5.49"
version = "4.5.53"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0025e98baa12e766c67ba13ff4695a887a1eba19569aad00a472546795bd6730"
checksum = "d76b5d13eaa18c901fd2f7fca939fefe3a0727a953561fefdf3b2922b8569d00"
dependencies = [
"anstream",
"anstyle",
@@ -602,21 +602,19 @@ dependencies = [
[[package]]
name = "dhcproto"
version = "0.14.0"
source = "git+https://github.com/bluecatengineering/dhcproto.git?branch=master#96a6845f7154a54e9c18d3595807c62e7994d5fe"
source = "git+https://github.com/bluecatengineering/dhcproto.git?branch=master#b4ea30defc01e7ae66e7075d6a0533d9bb9503dc"
dependencies = [
"dhcproto-macros",
"hex",
"hickory-proto",
"ipnet",
"rand",
"thiserror 2.0.12",
"url",
]
[[package]]
name = "dhcproto-macros"
version = "0.1.0"
source = "git+https://github.com/bluecatengineering/dhcproto.git?branch=master#96a6845f7154a54e9c18d3595807c62e7994d5fe"
version = "0.2.0"
source = "git+https://github.com/bluecatengineering/dhcproto.git?branch=master#b4ea30defc01e7ae66e7075d6a0533d9bb9503dc"
dependencies = [
"proc-macro2",
"quote",
@@ -1293,9 +1291,9 @@ checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe"
[[package]]
name = "libc"
version = "0.2.177"
version = "0.2.178"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2874a2af47a2325c2001a6e6fad9b16a53b802102b528163885171cf92b15976"
checksum = "37c93d8daa9d8a012fd8ab92f088405fb202ea0b6ab73ee2482ae66af4f42091"
[[package]]
name = "linux-raw-sys"
@@ -1344,9 +1342,9 @@ dependencies = [
[[package]]
name = "log"
version = "0.4.28"
version = "0.4.29"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "34080505efa8e45a4b816c349525ebe327ceaa8559756f0356cba97ef3bf7432"
checksum = "5e5032e24019045c762d3c0f28f5b6b8bbf38563a65908389bf7978758920897"
[[package]]
name = "mac_address"
@@ -1956,6 +1954,15 @@ version = "1.0.18"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f3cb5ba0dc43242ce17de99c180e96db90b235b8a9fdc9543c96d2209116bd9f"
[[package]]
name = "scc"
version = "2.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "46e6f046b7fef48e2660c57ed794263155d713de679057f2d0c169bfc6e756cc"
dependencies = [
"sdd",
]
[[package]]
name = "schannel"
version = "0.1.24"
@@ -1971,6 +1978,12 @@ version = "1.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49"
[[package]]
name = "sdd"
version = "3.0.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "490dcfcbfef26be6800d11870ff2df8774fa6e86d047e3e8c8a76b25655e41ca"
[[package]]
name = "security-framework"
version = "2.11.1"
@@ -2002,9 +2015,9 @@ checksum = "61697e0a1c7e512e84a621326239844a24d8207b4669b41bc18b32ea5cbf988b"
[[package]]
name = "sentry"
version = "0.45.0"
version = "0.46.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "48b85e25e8a1fc13928885e8bf13abe8a09e15c46993aed05d6405f7755d6e20"
checksum = "d9794f69ad475e76c057e326175d3088509649e3aed98473106b9fe94ba59424"
dependencies = [
"httpdate",
"native-tls",
@@ -2022,9 +2035,9 @@ dependencies = [
[[package]]
name = "sentry-actix"
version = "0.45.0"
version = "0.46.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cc694e6ffc8d5d7fdb2a33923b0358f6ad41c0b428ced034b349b9e2b08260bc"
checksum = "e0fee202934063ace4f1d1d063113b8982293762628e563a2d2fba08fb20b110"
dependencies = [
"actix-http",
"actix-web",
@@ -2035,9 +2048,9 @@ dependencies = [
[[package]]
name = "sentry-anyhow"
version = "0.45.0"
version = "0.46.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cfd12302213b0f449a065a68164e30dc02f5a4bc84843c2c5939b2c46b1513e8"
checksum = "d1832cd051f3198af8ebc5222da5fd213e49976d758b7defa7accbff3aed1909"
dependencies = [
"anyhow",
"sentry-backtrace",
@@ -2046,9 +2059,9 @@ dependencies = [
[[package]]
name = "sentry-backtrace"
version = "0.45.0"
version = "0.46.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f3253a495ab536f6de1746a58d5d7824b77d75e08e1a4b8ca6fb356839077ae0"
checksum = "e81137ad53b8592bd0935459ad74c0376053c40084aa170451e74eeea8dbc6c3"
dependencies = [
"backtrace",
"regex",
@@ -2057,9 +2070,9 @@ dependencies = [
[[package]]
name = "sentry-contexts"
version = "0.45.0"
version = "0.46.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "027f81a728836e66b88c07666a10f5ed5a35e2695b04eb7aa0fcbed93f814900"
checksum = "cfb403c66cc2651a01b9bacda2e7c22cd51f7e8f56f206aa4310147eb3259282"
dependencies = [
"hostname",
"libc",
@@ -2071,9 +2084,9 @@ dependencies = [
[[package]]
name = "sentry-core"
version = "0.45.0"
version = "0.46.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d3b6729c8e71ac968edbe9bf2dd4109c162e552b52bacd2b07e24ede1aba84a5"
checksum = "cfc409727ae90765ca8ea76fe6c949d6f159a11d02e130b357fa652ee9efcada"
dependencies = [
"rand",
"sentry-types",
@@ -2084,9 +2097,9 @@ dependencies = [
[[package]]
name = "sentry-debug-images"
version = "0.45.0"
version = "0.46.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dc85b59c1dfb19912bfba1af73a592e2e5548cae241a79ecb805afab3333d04c"
checksum = "06a2778a222fd90ebb01027c341a72f8e24b0c604c6126504a4fe34e5500e646"
dependencies = [
"findshlibs",
"sentry-core",
@@ -2094,9 +2107,9 @@ dependencies = [
[[package]]
name = "sentry-panic"
version = "0.45.0"
version = "0.46.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1ac0471f04f8f97af0c17eeca2c516e23faa1c0271a55bc64371d9ce488c2d40"
checksum = "3df79f4e1e72b2a8b75a0ebf49e78709ceb9b3f0b451f13adc92a0361b0aaabe"
dependencies = [
"sentry-backtrace",
"sentry-core",
@@ -2104,9 +2117,9 @@ dependencies = [
[[package]]
name = "sentry-tracing"
version = "0.45.0"
version = "0.46.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "428f780866a613142dcc81b7f8551ae4d1c056f4df22b6d7ddd9154a9974eb03"
checksum = "ff2046f527fd4b75e0b6ab3bd656c67dce42072f828dc4d03c206d15dca74a93"
dependencies = [
"bitflags 2.9.4",
"sentry-backtrace",
@@ -2117,9 +2130,9 @@ dependencies = [
[[package]]
name = "sentry-types"
version = "0.45.0"
version = "0.46.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2c19d1d1967b55659c358886d0f1aa3076488d445f84c7d727d384c675adaec1"
checksum = "c7b9b4e4c03a4d3643c18c78b8aa91d2cbee5da047d2fa0ca4bb29bc67e6c55c"
dependencies = [
"debugid",
"hex",
@@ -2178,27 +2191,27 @@ dependencies = [
[[package]]
name = "serial_test"
version = "0.10.0"
version = "3.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1c789ec87f4687d022a2405cf46e0cd6284889f1839de292cadeb6c6019506f2"
checksum = "1b258109f244e1d6891bf1053a55d63a5cd4f8f4c30cf9a1280989f80e7a1fa9"
dependencies = [
"dashmap",
"futures",
"lazy_static",
"log",
"once_cell",
"parking_lot",
"scc",
"serial_test_derive",
]
[[package]]
name = "serial_test_derive"
version = "0.10.0"
version = "3.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b64f9e531ce97c88b4778aad0ceee079216071cffec6ac9b904277f8f92e7fe3"
checksum = "5d69265a08751de7844521fd15003ae0a888e035773ba05695c5c759a6f89eef"
dependencies = [
"proc-macro2",
"quote",
"syn 1.0.109",
"syn 2.0.101",
]
[[package]]
@@ -2357,9 +2370,9 @@ dependencies = [
[[package]]
name = "system-configuration"
version = "0.6.1"
version = "0.7.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3c879d448e9d986b661742763247d3693ed13609438cf3d006f51f5368a5ba6b"
checksum = "a13f3d0daba03132c0aa9767f98351b3488edc2c100cda2d2ec2b04f3d8d3c8b"
dependencies = [
"bitflags 2.9.4",
"core-foundation",
@@ -2740,9 +2753,9 @@ checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a"
[[package]]
name = "vmnet"
version = "0.4.2"
version = "0.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c96eae216a9fa27c8e458ee5ef56dfc92d43fc8c25d67ade4a7fa83f0cb8b21b"
checksum = "4d88878a5583a43715c9f6de9b4cf94bc1af1dfec2a6abd75fd821de623f693b"
dependencies = [
"bitflags 1.3.2",
"block",
@@ -2758,9 +2771,9 @@ dependencies = [
[[package]]
name = "vmnet-derive"
version = "0.4.2"
version = "0.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fa20b8d2b5b0504355848b99b54fda9411f8201f44c1a9ee5a2d3c3134d31297"
checksum = "9ee4569490df90dfa2cfeda38230905b2331e72a16fa62a8f5d31069ea024db2"
dependencies = [
"darling",
"proc-macro2",
+3 -3
View File
@@ -16,7 +16,7 @@ smoltcp = "0"
libc = "0"
polling = "3"
dhcproto = { git = "https://github.com/bluecatengineering/dhcproto.git", branch = "master" }
vmnet = "0"
vmnet = "0.5.0"
clap = { version = "4", features = ["derive"] }
mac_address = "1"
privdrop = "0"
@@ -31,8 +31,8 @@ nix = { version = "0", features = ["signal", "socket"] }
prefix-trie = "0"
ipnet = "2"
oslog = "0.2.0"
log = "0.4.28"
serial_test = "0"
log = "0.4.29"
serial_test = "3"
[profile.release]
debug = true
+14 -3
View File
@@ -9,7 +9,7 @@ use std::sync::mpsc::{SyncSender, sync_channel};
use vmnet::mode::Mode;
use vmnet::parameters::{Parameter, ParameterKind};
use vmnet::port_forwarding::{AddressFamily, Protocol};
use vmnet::{Events, Options};
use vmnet::{Batch, Events, Options};
#[derive(ValueEnum, Clone, Debug)]
pub enum NetType {
@@ -29,6 +29,7 @@ pub struct Host {
callback_can_continue_tx: SyncSender<()>,
pub gateway_ip: smoltcp::wire::Ipv4Address,
pub max_packet_size: u64,
pub read_max_packets: u64,
finalized: bool,
}
@@ -67,6 +68,15 @@ impl Host {
));
};
// Retrieve read max packets for this interface
let Some(Parameter::ReadMaxPackets(read_max_packets)) =
interface.parameters().get(ParameterKind::ReadMaxPackets)
else {
return Err(anyhow!(
"failed to retrieve vmnet's interface read max packets"
));
};
// Set up a socketpair() to emulate polling of the vmnet interface
let (new_packets_tx, new_packets_rx) = UnixDatagram::pair()?;
new_packets_rx.set_nonblocking(true)?;
@@ -96,6 +106,7 @@ impl Host {
callback_can_continue_tx,
gateway_ip,
max_packet_size,
read_max_packets,
finalized: false,
})
}
@@ -133,14 +144,14 @@ impl Host {
.map_err(|err| anyhow!("failed to remove port forwarding rule {details}: {err}"))
}
pub fn read(&mut self, buf: &mut [u8]) -> vmnet::Result<usize> {
pub fn read(&mut self, batch: &mut Batch, bufs: &mut [Vec<u8>]) -> vmnet::Result<usize> {
// Dequeue dummy datagram from the socket (if any)
// to free up buffer space and reduce false-positives
// when polling
let mut buf_to_be_discarded: [u8; 1] = [0; 1];
let _ = self.new_packets_rx.recv(&mut buf_to_be_discarded);
let result = self.interface.read(buf);
let result = self.interface.read_batch(batch, bufs);
if let Err(vmnet::Error::VmnetReadNothing) = result {
// We've emptied everything, unlock the callback
+17 -6
View File
@@ -18,6 +18,7 @@ use prefix_trie::{Prefix, PrefixMap, PrefixSet};
use smoltcp::wire::EthernetFrame;
use std::io::ErrorKind;
use std::os::unix::io::{AsRawFd, RawFd};
use vmnet::Batch;
pub struct Proxy<'proxy> {
vm: VM,
@@ -76,8 +77,16 @@ impl Proxy<'_> {
}
pub fn run(&mut self) -> Result<()> {
// Create a single buffer from reading from the VM
let mut buf: Vec<u8> = vec![0; self.host.max_packet_size as usize];
// Create multiple buffers and a batch for reading from the host
let mut bufs = vec![
vec![0u8; self.host.max_packet_size as usize];
self.host.read_max_packets as usize
];
let mut batch = Batch::preallocate(bufs.len());
self.poller.arm()?;
loop {
@@ -88,7 +97,7 @@ impl Proxy<'_> {
}
if host_readable {
self.read_from_host(buf.as_mut_slice())?;
self.read_from_host(&mut batch, &mut bufs)?;
}
// Graceful termination
@@ -125,12 +134,14 @@ impl Proxy<'_> {
}
}
fn read_from_host(&mut self, buf: &mut [u8]) -> Result<()> {
fn read_from_host(&mut self, batch: &mut Batch, bufs: &mut [Vec<u8>]) -> Result<()> {
loop {
match self.host.read(buf) {
Ok(n) => {
if let Ok(pkt) = EthernetFrame::new_checked(&buf[..n]) {
self.process_frame_from_host(&pkt)?;
match self.host.read(batch, bufs) {
Ok(pktcnt) => {
for buf in batch.packet_sized_bufs(bufs).take(pktcnt) {
if let Ok(pkt) = EthernetFrame::new_checked(buf) {
self.process_frame_from_host(&pkt)?;
}
}
}
Err(err) => {
+101 -12
View File
@@ -1,4 +1,4 @@
use anyhow::{Context, anyhow};
use anyhow::{Context, Error, anyhow};
use clap::Parser;
use ipnet::Ipv4Net;
use log::LevelFilter;
@@ -11,10 +11,12 @@ use softnet::proxy::ExposedPort;
use softnet::proxy::Proxy;
use std::borrow::Cow;
use std::env;
use std::net::{Ipv4Addr, SocketAddr, ToSocketAddrs};
use std::os::raw::c_int;
use std::os::unix::io::RawFd;
use std::os::unix::process::CommandExt;
use std::process::{Command, ExitCode};
use std::str::FromStr;
use system_configuration::core_foundation::base::TCFType;
use system_configuration::core_foundation::dictionary::CFDictionary;
use system_configuration::core_foundation::number::CFNumber;
@@ -23,6 +25,67 @@ use system_configuration::preferences::SCPreferences;
use system_configuration::sys::preferences::{SCPreferencesCommitChanges, SCPreferencesSetValue};
use uzers::{get_current_groupname, get_current_username, get_effective_uid};
#[derive(Debug, Clone)]
enum AllowBlockEntry {
Net(Ipv4Net),
Domain(String),
}
impl FromStr for AllowBlockEntry {
type Err = Error;
fn from_str(s: &str) -> Result<Self, Self::Err> {
let trimmed = s.trim();
if trimmed.is_empty() {
return Err(anyhow!("empty allow/block entry"));
}
if let Ok(net) = trimmed.parse::<Ipv4Net>() {
return Ok(AllowBlockEntry::Net(net));
}
if let Ok(addr) = trimmed.parse::<Ipv4Addr>() {
return Ok(AllowBlockEntry::Net(Ipv4Net::from(addr)));
}
Ok(AllowBlockEntry::Domain(trimmed.to_string()))
}
}
fn resolve_allow_block_entries(
kind: &str,
entries: Vec<AllowBlockEntry>,
) -> anyhow::Result<Vec<Ipv4Net>> {
let mut nets = Vec::new();
for entry in entries {
match entry {
AllowBlockEntry::Net(net) => nets.push(net),
AllowBlockEntry::Domain(domain) => {
// A-record resolution happens here via ToSocketAddrs, then we keep only IPv4s.
let resolved: Vec<Ipv4Net> = (domain.as_str(), 0)
.to_socket_addrs()
.with_context(|| format!("failed to resolve {kind} entry {domain}"))?
.filter_map(|addr| match addr {
SocketAddr::V4(v4) => Some(Ipv4Net::from(*v4.ip())),
SocketAddr::V6(_) => None,
})
.collect();
if resolved.is_empty() {
return Err(anyhow!(
"no IPv4 addresses found for {kind} entry {domain}"
));
}
nets.extend(resolved);
}
}
}
Ok(nets)
}
#[derive(Parser, Debug)]
struct Args {
#[clap(
@@ -52,30 +115,32 @@ struct Args {
#[clap(
long,
help = "Comma-separated list of CIDRs to allow the traffic to \
(e.g. --allow=192.168.0.0/24 may be used to allow a LAN access for a VM). \
help = "Comma-separated list of CIDRs, IPs, or domains to allow the traffic to \
(e.g. --allow=192.168.0.0/24 or --allow=example.com). Domains are resolved to A records \
at startup. \
When used with --block, the longest prefix match always wins. \
In case an identical prefix is both --allow'ed and --block'ed, \
blocking will take precedence. --allow=0.0.0.0/0 is a special case, \
it additionally disables bridge isolation (even when --block=0.0.0.0/0 is specified).",
value_name = "comma-separated CIDRs",
value_name = "comma-separated CIDRs/IPs/domains",
use_value_delimiter = true,
action = clap::ArgAction::Set
)]
allow: Vec<Ipv4Net>,
allow: Vec<AllowBlockEntry>,
#[clap(
long,
help = "Comma-separated list of CIDRs to block the traffic to \
help = "Comma-separated list of CIDRs, IPs, or domains to block the traffic to \
(e.g. --block=0.0.0.0/0 may be used to establish a default deny policy \
that is further relaxed with --allow). When used with --allow, \
that is further relaxed with --allow). Domains are resolved to A records at startup. \
When used with --allow, \
the longest prefix match always wins. In case the same prefix is both \
--allow'ed and --block'ed, blocking takes precedence.",
value_name = "comma-separated CIDRs",
value_name = "comma-separated CIDRs/IPs/domains",
use_value_delimiter = true,
action = clap::ArgAction::Set
)]
block: Vec<Ipv4Net>,
block: Vec<AllowBlockEntry>,
#[clap(
long,
@@ -145,7 +210,7 @@ fn try_main() -> anyhow::Result<()> {
// [2]: https://developer.apple.com/library/archive/documentation/System/Conceptual/ManPages_iPhoneOS/man2/kqueue.2.html
unsafe { signal(Signal::SIGINT, SigHandler::SigIgn) }?;
let args: Args = Args::parse();
let mut args: Args = Args::parse();
// No need to run anything, just return
// so that the invoker process knows we
@@ -188,6 +253,9 @@ fn try_main() -> anyhow::Result<()> {
));
}
let allow = resolve_allow_block_entries("allow", std::mem::take(&mut args.allow))?;
let block = resolve_allow_block_entries("block", std::mem::take(&mut args.block))?;
// Set bootpd(8) min/max lease time while still having the root privileges
set_bootpd_lease_time(args.bootpd_lease_time);
@@ -196,8 +264,8 @@ fn try_main() -> anyhow::Result<()> {
args.vm_fd as RawFd,
args.vm_mac_address,
args.vm_net_type,
PrefixSet::from_iter(args.allow),
PrefixSet::from_iter(args.block),
PrefixSet::from_iter(allow),
PrefixSet::from_iter(block),
args.expose,
)
.context("failed to initialize proxy")?;
@@ -249,3 +317,24 @@ fn set_bootpd_lease_time(lease_time: u32) {
SCPreferencesCommitChanges(prefs.as_concrete_TypeRef());
}
}
#[cfg(test)]
mod tests {
use super::{AllowBlockEntry, resolve_allow_block_entries};
use ipnet::Ipv4Net;
use std::net::Ipv4Addr;
#[test]
fn resolve_domain_to_ipv4_nets_dns_google() {
let nets = resolve_allow_block_entries(
"allow",
vec![AllowBlockEntry::Domain("dns.google".to_string())],
)
.unwrap();
assert!(
nets.contains(&Ipv4Net::from(Ipv4Addr::new(8, 8, 8, 8)))
|| nets.contains(&Ipv4Net::from(Ipv4Addr::new(8, 8, 4, 4)))
);
}
}