mirror of
https://github.com/EasyTier/EasyTier.git
synced 2026-10-29 16:05:35 +00:00
Compare commits
4
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
7a9b3d7b28 | ||
|
|
12bfb8dbec | ||
|
|
372b6e15fe | ||
|
|
be0b4c7512 |
@@ -51,6 +51,11 @@ jobs:
|
|||||||
pnpm: true
|
pnpm: true
|
||||||
token: ${{ secrets.GITHUB_TOKEN }}
|
token: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
|
||||||
|
- name: Install LTTng UST headers
|
||||||
|
run: |
|
||||||
|
sudo apt-get update
|
||||||
|
sudo apt-get install -y liblttng-ust-dev
|
||||||
|
|
||||||
- uses: actions-rust-lang/setup-rust-toolchain@v1
|
- uses: actions-rust-lang/setup-rust-toolchain@v1
|
||||||
with:
|
with:
|
||||||
components: rustfmt,clippy
|
components: rustfmt,clippy
|
||||||
|
|||||||
Generated
+63
-137
@@ -129,12 +129,6 @@ dependencies = [
|
|||||||
"libc",
|
"libc",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "anes"
|
|
||||||
version = "0.1.6"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "4b46cbb362ab8752921c97e041f5e366ee6297bd428a31275b9fcf1e380f7299"
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "anstream"
|
name = "anstream"
|
||||||
version = "0.6.15"
|
version = "0.6.15"
|
||||||
@@ -778,6 +772,29 @@ dependencies = [
|
|||||||
"serde",
|
"serde",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "bindgen"
|
||||||
|
version = "0.65.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "cfdf7b466f9a4903edc73f95d6d2bcd5baf8ae620638762244d3f60143643cc5"
|
||||||
|
dependencies = [
|
||||||
|
"bitflags 1.3.2",
|
||||||
|
"cexpr",
|
||||||
|
"clang-sys",
|
||||||
|
"lazy_static",
|
||||||
|
"lazycell",
|
||||||
|
"log",
|
||||||
|
"peeking_take_while",
|
||||||
|
"prettyplease",
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"regex",
|
||||||
|
"rustc-hash 1.1.0",
|
||||||
|
"shlex",
|
||||||
|
"syn 2.0.117",
|
||||||
|
"which 4.4.2",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "bindgen"
|
name = "bindgen"
|
||||||
version = "0.72.1"
|
version = "0.72.1"
|
||||||
@@ -791,7 +808,7 @@ dependencies = [
|
|||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
"regex",
|
"regex",
|
||||||
"rustc-hash",
|
"rustc-hash 2.1.0",
|
||||||
"shlex",
|
"shlex",
|
||||||
"syn 2.0.117",
|
"syn 2.0.117",
|
||||||
]
|
]
|
||||||
@@ -1145,12 +1162,6 @@ dependencies = [
|
|||||||
"toml 0.9.12+spec-1.1.0",
|
"toml 0.9.12+spec-1.1.0",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "cast"
|
|
||||||
version = "0.3.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "37b2a672a2cb129a2e41c10b1224bb368f9f37a2b16b612598138befd7b37eb5"
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "cc"
|
name = "cc"
|
||||||
version = "1.2.10"
|
version = "1.2.10"
|
||||||
@@ -1260,33 +1271,6 @@ dependencies = [
|
|||||||
"windows-targets 0.52.6",
|
"windows-targets 0.52.6",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "ciborium"
|
|
||||||
version = "0.2.2"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "42e69ffd6f0917f5c029256a24d0161db17cea3997d185db0d35926308770f0e"
|
|
||||||
dependencies = [
|
|
||||||
"ciborium-io",
|
|
||||||
"ciborium-ll",
|
|
||||||
"serde",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "ciborium-io"
|
|
||||||
version = "0.2.2"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "05afea1e0a06c9be33d539b876f1ce3692f4afea2cb41f740e7743225ed1c757"
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "ciborium-ll"
|
|
||||||
version = "0.2.2"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "57663b653d948a338bfb3eeba9bb2fd5fcfaecb9e199e87e1eda4d9e8b240fd9"
|
|
||||||
dependencies = [
|
|
||||||
"ciborium-io",
|
|
||||||
"half",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "cidr"
|
name = "cidr"
|
||||||
version = "0.3.1"
|
version = "0.3.1"
|
||||||
@@ -1632,42 +1616,6 @@ dependencies = [
|
|||||||
"cfg-if",
|
"cfg-if",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "criterion"
|
|
||||||
version = "0.5.1"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "f2b12d017a929603d80db1831cd3a24082f8137ce19c69e6447f54f5fc8d692f"
|
|
||||||
dependencies = [
|
|
||||||
"anes",
|
|
||||||
"cast",
|
|
||||||
"ciborium",
|
|
||||||
"clap",
|
|
||||||
"criterion-plot",
|
|
||||||
"is-terminal",
|
|
||||||
"itertools 0.10.5",
|
|
||||||
"num-traits",
|
|
||||||
"once_cell",
|
|
||||||
"oorandom",
|
|
||||||
"plotters",
|
|
||||||
"rayon",
|
|
||||||
"regex",
|
|
||||||
"serde",
|
|
||||||
"serde_derive",
|
|
||||||
"serde_json",
|
|
||||||
"tinytemplate",
|
|
||||||
"walkdir",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "criterion-plot"
|
|
||||||
version = "0.5.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "6b50826342786a51a89e2da3a28f1c32b06e387201bc2d19791f622c673706b1"
|
|
||||||
dependencies = [
|
|
||||||
"cast",
|
|
||||||
"itertools 0.10.5",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "critical-section"
|
name = "critical-section"
|
||||||
version = "1.2.0"
|
version = "1.2.0"
|
||||||
@@ -2341,7 +2289,6 @@ dependencies = [
|
|||||||
"clap_complete",
|
"clap_complete",
|
||||||
"clap_complete_nushell",
|
"clap_complete_nushell",
|
||||||
"console-subscriber",
|
"console-subscriber",
|
||||||
"criterion",
|
|
||||||
"crossbeam",
|
"crossbeam",
|
||||||
"ctor 0.8.0",
|
"ctor 0.8.0",
|
||||||
"dashmap",
|
"dashmap",
|
||||||
@@ -2374,6 +2321,8 @@ dependencies = [
|
|||||||
"indoc",
|
"indoc",
|
||||||
"itertools 0.14.0",
|
"itertools 0.14.0",
|
||||||
"kcp-sys",
|
"kcp-sys",
|
||||||
|
"lttng-ust",
|
||||||
|
"lttng-ust-generate",
|
||||||
"machine-uid",
|
"machine-uid",
|
||||||
"maplit",
|
"maplit",
|
||||||
"mimalloc",
|
"mimalloc",
|
||||||
@@ -4572,17 +4521,6 @@ dependencies = [
|
|||||||
"once_cell",
|
"once_cell",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "is-terminal"
|
|
||||||
version = "0.4.17"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "3640c1c38b8e4e43584d8df18be5fc6b0aa314ce6ebf51b53313d4306cca8e46"
|
|
||||||
dependencies = [
|
|
||||||
"hermit-abi",
|
|
||||||
"libc",
|
|
||||||
"windows-sys 0.61.2",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "is-wsl"
|
name = "is-wsl"
|
||||||
version = "0.4.0"
|
version = "0.4.0"
|
||||||
@@ -4751,7 +4689,7 @@ source = "git+https://github.com/EasyTier/kcp-sys?rev=d7427c22d764deb1860a7d37ac
|
|||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"auto_impl",
|
"auto_impl",
|
||||||
"bindgen",
|
"bindgen 0.72.1",
|
||||||
"bitflags 2.8.0",
|
"bitflags 2.8.0",
|
||||||
"bytes",
|
"bytes",
|
||||||
"cc",
|
"cc",
|
||||||
@@ -4798,6 +4736,12 @@ dependencies = [
|
|||||||
"spin",
|
"spin",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "lazycell"
|
||||||
|
version = "1.3.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "830d08ce1d1d941e6b30645f1a0eb5643013d835ce3779a5fc208261dbe10f55"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "leb128fmt"
|
name = "leb128fmt"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
@@ -4861,7 +4805,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||||||
checksum = "4979f22fdb869068da03c9f7528f8297c6fd2606bc3a4affe42e6a823fdb8da4"
|
checksum = "4979f22fdb869068da03c9f7528f8297c6fd2606bc3a4affe42e6a823fdb8da4"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"cfg-if",
|
"cfg-if",
|
||||||
"windows-targets 0.52.6",
|
"windows-targets 0.48.5",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -4991,6 +4935,20 @@ version = "0.1.2"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "112b39cec0b298b6c1999fee3e31427f74f676e4cb9879ed1a121b43661a4154"
|
checksum = "112b39cec0b298b6c1999fee3e31427f74f676e4cb9879ed1a121b43661a4154"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "lttng-ust"
|
||||||
|
version = "0.1.0"
|
||||||
|
source = "git+https://github.com/EasyTier/lttng-ust-rs.git#fb3a7a86e9c3d8edad34cc70fd48f908b745f8d9"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "lttng-ust-generate"
|
||||||
|
version = "0.1.1"
|
||||||
|
source = "git+https://github.com/EasyTier/lttng-ust-rs.git#fb3a7a86e9c3d8edad34cc70fd48f908b745f8d9"
|
||||||
|
dependencies = [
|
||||||
|
"bindgen 0.65.1",
|
||||||
|
"cc",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "mac"
|
name = "mac"
|
||||||
version = "0.1.1"
|
version = "0.1.1"
|
||||||
@@ -5943,12 +5901,6 @@ dependencies = [
|
|||||||
"portable-atomic",
|
"portable-atomic",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "oorandom"
|
|
||||||
version = "11.1.5"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "d6790f58c7ff633d8771f42965289203411a5e5c68388703c06e14f24770b41e"
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "opaque-debug"
|
name = "opaque-debug"
|
||||||
version = "0.3.1"
|
version = "0.3.1"
|
||||||
@@ -6316,6 +6268,12 @@ dependencies = [
|
|||||||
"hmac",
|
"hmac",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "peeking_take_while"
|
||||||
|
version = "0.1.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "19b17cddbe7ec3f8bc800887bab5e717348c95ea2ca0b1bf0837fb964dc67099"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "pem"
|
name = "pem"
|
||||||
version = "3.0.4"
|
version = "3.0.4"
|
||||||
@@ -6579,34 +6537,6 @@ dependencies = [
|
|||||||
"time",
|
"time",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "plotters"
|
|
||||||
version = "0.3.7"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "5aeb6f403d7a4911efb1e33402027fc44f29b5bf6def3effcc22d7bb75f2b747"
|
|
||||||
dependencies = [
|
|
||||||
"num-traits",
|
|
||||||
"plotters-backend",
|
|
||||||
"plotters-svg",
|
|
||||||
"wasm-bindgen",
|
|
||||||
"web-sys",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "plotters-backend"
|
|
||||||
version = "0.3.7"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "df42e13c12958a16b3f7f4386b9ab1f3e7933914ecea48da7139435263a4172a"
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "plotters-svg"
|
|
||||||
version = "0.3.7"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "51bae2ac328883f7acdfea3d66a7c35751187f870bc81f94563733a154d7a670"
|
|
||||||
dependencies = [
|
|
||||||
"plotters-backend",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "pnet"
|
name = "pnet"
|
||||||
version = "0.35.0"
|
version = "0.35.0"
|
||||||
@@ -7188,7 +7118,7 @@ dependencies = [
|
|||||||
"pin-project-lite",
|
"pin-project-lite",
|
||||||
"quinn-proto",
|
"quinn-proto",
|
||||||
"quinn-udp",
|
"quinn-udp",
|
||||||
"rustc-hash",
|
"rustc-hash 2.1.0",
|
||||||
"rustls",
|
"rustls",
|
||||||
"socket2 0.5.10",
|
"socket2 0.5.10",
|
||||||
"thiserror 2.0.11",
|
"thiserror 2.0.11",
|
||||||
@@ -7209,7 +7139,7 @@ dependencies = [
|
|||||||
"lru-slab",
|
"lru-slab",
|
||||||
"rand 0.9.1",
|
"rand 0.9.1",
|
||||||
"ring",
|
"ring",
|
||||||
"rustc-hash",
|
"rustc-hash 2.1.0",
|
||||||
"rustls",
|
"rustls",
|
||||||
"rustls-pki-types",
|
"rustls-pki-types",
|
||||||
"rustls-platform-verifier",
|
"rustls-platform-verifier",
|
||||||
@@ -7896,6 +7826,12 @@ dependencies = [
|
|||||||
"wasm-bindgen",
|
"wasm-bindgen",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "rustc-hash"
|
||||||
|
version = "1.1.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "rustc-hash"
|
name = "rustc-hash"
|
||||||
version = "2.1.0"
|
version = "2.1.0"
|
||||||
@@ -9985,16 +9921,6 @@ dependencies = [
|
|||||||
"zerovec",
|
"zerovec",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "tinytemplate"
|
|
||||||
version = "1.2.1"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "be4d6b5f19ff7664e8c98d03e2139cb510db9b0a60b55f8e8709b689d939b6bc"
|
|
||||||
dependencies = [
|
|
||||||
"serde",
|
|
||||||
"serde_json",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "tinyvec"
|
name = "tinyvec"
|
||||||
version = "1.8.0"
|
version = "1.8.0"
|
||||||
@@ -11399,7 +11325,7 @@ version = "0.1.9"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "cf221c93e13a30d793f7645a0e7762c55d169dbb0a49671918a2319d289b10bb"
|
checksum = "cf221c93e13a30d793f7645a0e7762c55d169dbb0a49671918a2319d289b10bb"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"windows-sys 0.59.0",
|
"windows-sys 0.48.0",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
|||||||
@@ -11,17 +11,8 @@ const props = defineProps<{
|
|||||||
|
|
||||||
const list = defineModel<string[]>({ required: true })
|
const list = defineModel<string[]>({ required: true })
|
||||||
|
|
||||||
const fallbackUrl = () => {
|
|
||||||
const protoKeys = Object.keys(props.protos)
|
|
||||||
const defaultProto = protoKeys.includes('tcp')
|
|
||||||
? 'tcp'
|
|
||||||
: (protoKeys[0] ?? 'tcp')
|
|
||||||
const defaultPort = props.protos[defaultProto] ?? 11010
|
|
||||||
return `${defaultProto}://0.0.0.0:${defaultPort}`
|
|
||||||
}
|
|
||||||
|
|
||||||
const addUrl = () => {
|
const addUrl = () => {
|
||||||
list.value.push(props.defaultUrl || fallbackUrl())
|
list.value.push(props.defaultUrl || 'tcp://0.0.0.0:11010')
|
||||||
}
|
}
|
||||||
|
|
||||||
const removeUrl = (index: number) => {
|
const removeUrl = (index: number) => {
|
||||||
|
|||||||
@@ -1,93 +0,0 @@
|
|||||||
import { mount } from '@vue/test-utils'
|
|
||||||
import { describe, expect, it } from 'vitest'
|
|
||||||
import { defineComponent, h, nextTick, ref } from 'vue'
|
|
||||||
import UrlListInput from '../src/components/UrlListInput.vue'
|
|
||||||
|
|
||||||
const ButtonStub = defineComponent({
|
|
||||||
name: 'Button',
|
|
||||||
emits: ['click'],
|
|
||||||
setup(_, { slots, emit }) {
|
|
||||||
return () => h('button', { onClick: (event: MouseEvent) => emit('click', event) }, slots.default?.())
|
|
||||||
},
|
|
||||||
})
|
|
||||||
|
|
||||||
const UrlInputStub = defineComponent({
|
|
||||||
name: 'UrlInput',
|
|
||||||
setup(_, { slots }) {
|
|
||||||
return () => h('div', slots.actions?.())
|
|
||||||
},
|
|
||||||
})
|
|
||||||
|
|
||||||
function mountUrlListInput(protos: Record<string, number>, defaultUrl?: string) {
|
|
||||||
const urls = ref<string[]>([])
|
|
||||||
const wrapper = mount(defineComponent({
|
|
||||||
components: { UrlListInput },
|
|
||||||
setup() {
|
|
||||||
return { urls, protos, defaultUrl }
|
|
||||||
},
|
|
||||||
template: `
|
|
||||||
<UrlListInput
|
|
||||||
v-model="urls"
|
|
||||||
:protos="protos"
|
|
||||||
:default-url="defaultUrl"
|
|
||||||
add-label="add_url"
|
|
||||||
/>
|
|
||||||
`,
|
|
||||||
}), {
|
|
||||||
global: {
|
|
||||||
stubs: {
|
|
||||||
Button: ButtonStub,
|
|
||||||
UrlInput: UrlInputStub,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
})
|
|
||||||
|
|
||||||
return { wrapper, urls }
|
|
||||||
}
|
|
||||||
|
|
||||||
describe('UrlListInput.vue add fallback', () => {
|
|
||||||
it('derives the fallback URL from protos when defaultUrl is not provided', async () => {
|
|
||||||
const { wrapper, urls } = mountUrlListInput({ tcp: 11010, udp: 11010 })
|
|
||||||
|
|
||||||
await wrapper.find('.cursor-pointer').trigger('click')
|
|
||||||
await nextTick()
|
|
||||||
|
|
||||||
expect(urls.value).toEqual(['tcp://0.0.0.0:11010'])
|
|
||||||
})
|
|
||||||
|
|
||||||
it('falls back to the first available protocol when tcp is not present', async () => {
|
|
||||||
const { wrapper, urls } = mountUrlListInput({ udp: 22000 })
|
|
||||||
|
|
||||||
await wrapper.find('.cursor-pointer').trigger('click')
|
|
||||||
await nextTick()
|
|
||||||
|
|
||||||
expect(urls.value).toEqual(['udp://0.0.0.0:22000'])
|
|
||||||
})
|
|
||||||
|
|
||||||
it('falls back to tcp default port when protos is empty', async () => {
|
|
||||||
const { wrapper, urls } = mountUrlListInput({})
|
|
||||||
|
|
||||||
await wrapper.find('.cursor-pointer').trigger('click')
|
|
||||||
await nextTick()
|
|
||||||
|
|
||||||
expect(urls.value).toEqual(['tcp://0.0.0.0:11010'])
|
|
||||||
})
|
|
||||||
|
|
||||||
it('supports port-zero fallback from protos', async () => {
|
|
||||||
const { wrapper, urls } = mountUrlListInput({ tcp: 0, udp: 0 })
|
|
||||||
|
|
||||||
await wrapper.find('.cursor-pointer').trigger('click')
|
|
||||||
await nextTick()
|
|
||||||
|
|
||||||
expect(urls.value).toEqual(['tcp://0.0.0.0:0'])
|
|
||||||
})
|
|
||||||
|
|
||||||
it('uses defaultUrl when provided', async () => {
|
|
||||||
const { wrapper, urls } = mountUrlListInput({ tcp: 11010 }, 'udp://0.0.0.0:22000')
|
|
||||||
|
|
||||||
await wrapper.find('.cursor-pointer').trigger('click')
|
|
||||||
await nextTick()
|
|
||||||
|
|
||||||
expect(urls.value).toEqual(['udp://0.0.0.0:22000'])
|
|
||||||
})
|
|
||||||
})
|
|
||||||
+3
-15
@@ -28,14 +28,6 @@ path = "src/easytier-cli.rs"
|
|||||||
name = "easytier"
|
name = "easytier"
|
||||||
path = "src/lib.rs"
|
path = "src/lib.rs"
|
||||||
|
|
||||||
[[bench]]
|
|
||||||
name = "tx_throughput"
|
|
||||||
harness = false
|
|
||||||
|
|
||||||
[[bench]]
|
|
||||||
name = "packet_bytes_extraction"
|
|
||||||
harness = false
|
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
git-version = "0.3.9"
|
git-version = "0.3.9"
|
||||||
|
|
||||||
@@ -45,6 +37,7 @@ tracing-subscriber = { version = "0.3", features = [
|
|||||||
"local-time",
|
"local-time",
|
||||||
"time",
|
"time",
|
||||||
] }
|
] }
|
||||||
|
lttng-ust = { git = "https://github.com/EasyTier/lttng-ust-rs.git", optional = true }
|
||||||
derivative = "2.2.0"
|
derivative = "2.2.0"
|
||||||
derive_more = { version = "2.1.1", features = ["full"] }
|
derive_more = { version = "2.1.1", features = ["full"] }
|
||||||
console-subscriber = { version = "0.4.1", optional = true }
|
console-subscriber = { version = "0.4.1", optional = true }
|
||||||
@@ -337,6 +330,7 @@ quote = "1"
|
|||||||
thunk-rs = { git = "https://github.com/easytier/thunk.git", default-features = false, features = [
|
thunk-rs = { git = "https://github.com/easytier/thunk.git", default-features = false, features = [
|
||||||
"win7",
|
"win7",
|
||||||
] }
|
] }
|
||||||
|
lttng-ust-generate = { git = "https://github.com/EasyTier/lttng-ust-rs.git", optional = true }
|
||||||
|
|
||||||
[target.'cfg(windows)'.build-dependencies]
|
[target.'cfg(windows)'.build-dependencies]
|
||||||
reqwest = { version = "0.12.12", features = ["blocking"] }
|
reqwest = { version = "0.12.12", features = ["blocking"] }
|
||||||
@@ -344,7 +338,6 @@ zip = "4.0.0"
|
|||||||
|
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
criterion = "0.5.1"
|
|
||||||
serial_test = "3.0.0"
|
serial_test = "3.0.0"
|
||||||
rstest = "0.25.0"
|
rstest = "0.25.0"
|
||||||
futures-util = "0.3.31"
|
futures-util = "0.3.31"
|
||||||
@@ -411,14 +404,9 @@ jemalloc-prof = [
|
|||||||
"jemalloc-sys/stats",
|
"jemalloc-sys/stats",
|
||||||
]
|
]
|
||||||
tracing = ["tokio/tracing", "dep:console-subscriber"]
|
tracing = ["tokio/tracing", "dep:console-subscriber"]
|
||||||
|
lttng = ["dep:lttng-ust", "dep:lttng-ust-generate"]
|
||||||
magic-dns = ["dep:hickory-client", "dep:hickory-server"]
|
magic-dns = ["dep:hickory-client", "dep:hickory-server"]
|
||||||
faketcp = ["dep:flume"]
|
faketcp = ["dep:flume"]
|
||||||
zstd = ["dep:zstd"]
|
zstd = ["dep:zstd"]
|
||||||
# Deprecated: hotpath profiling has been removed. These feature aliases are
|
|
||||||
# retained as no-ops so existing build scripts using `--features hotpath*`
|
|
||||||
# continue to work without pulling in any dependencies.
|
|
||||||
hotpath = []
|
|
||||||
hotpath-cpu = ["hotpath"]
|
|
||||||
hotpath-alloc = ["hotpath"]
|
|
||||||
# For Network Extension on macOS
|
# For Network Extension on macOS
|
||||||
macos-ne = []
|
macos-ne = []
|
||||||
|
|||||||
@@ -1,162 +0,0 @@
|
|||||||
# Benchmarks
|
|
||||||
|
|
||||||
Criterion benchmarks for EasyTier hot paths.
|
|
||||||
|
|
||||||
| Bench | What it measures |
|
|
||||||
| --------------------------- | -------------------------------------------------------------------------------- |
|
|
||||||
| `tx_throughput` | End-to-end TX injection path through `peer_manager::send_msg_by_ip` |
|
|
||||||
| `packet_bytes_extraction` | `ZCPacket::payload_bytes` / `tunnel_payload_bytes` extraction (advance hot path) |
|
|
||||||
|
|
||||||
## Packet Bytes Extraction
|
|
||||||
|
|
||||||
Criterion benchmark for `ZCPacket` bytes extraction — the methods touched by the
|
|
||||||
`advance`-based slicing refactor. Measures `payload_bytes` and
|
|
||||||
`tunnel_payload_bytes` at two payload sizes (1280, 4096). Setup
|
|
||||||
(`ZCPacket::new_with_payload`) runs in the benchmark harness's preparation
|
|
||||||
phase and is excluded from the timed region, so the numbers reflect only the
|
|
||||||
extraction call.
|
|
||||||
|
|
||||||
### Quick start
|
|
||||||
|
|
||||||
```bash
|
|
||||||
cargo bench --bench packet_bytes_extraction
|
|
||||||
```
|
|
||||||
|
|
||||||
Smoke run:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
PACKET_BYTES_MEASUREMENT_SECS=2 \
|
|
||||||
PACKET_BYTES_WARMUP_SECS=1 \
|
|
||||||
PACKET_BYTES_SAMPLE_SIZE=10 \
|
|
||||||
cargo bench --bench packet_bytes_extraction -- --quiet
|
|
||||||
```
|
|
||||||
|
|
||||||
### Environment variables
|
|
||||||
|
|
||||||
| Variable | Default | Notes |
|
|
||||||
| ------------------------------- | ------- | ---------------------------- |
|
|
||||||
| `PACKET_BYTES_MEASUREMENT_SECS` | `10` | Criterion `measurement_time` |
|
|
||||||
| `PACKET_BYTES_WARMUP_SECS` | `3` | Criterion `warm_up_time` |
|
|
||||||
| `PACKET_BYTES_SAMPLE_SIZE` | `10` | Criterion `sample_size` (min 10) |
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## TX Throughput Benchmark
|
|
||||||
|
|
||||||
Criterion benchmark for EasyTier's TX injection path (`peer_manager::send_msg_by_ip`).
|
|
||||||
|
|
||||||
## What it measures
|
|
||||||
|
|
||||||
The benchmark sets up two EasyTier instances (`hot-a` / `hot-b`) and drives
|
|
||||||
packets from `hot-a` to `hot-b` via `peer_manager.send_msg_by_ip`. This is the
|
|
||||||
same entry point `easytier-core` uses for daily forwarded traffic, so the
|
|
||||||
numbers reflect the real TX hot path: NIC pipeline → route lookup →
|
|
||||||
compress/encrypt → peer connection → tunnel send.
|
|
||||||
|
|
||||||
Two variants are reported per tunnel kind:
|
|
||||||
|
|
||||||
| Bench | What it measures |
|
|
||||||
| --------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
||||||
| `tx_throughput/<tunnel>` | Serial baseline. One send in flight at a time. Reports per-packet CPU cost (TX injection latency). |
|
|
||||||
| `tx_throughput/<tunnel>-saturate` | Spawns `TX_THROUGHPUT_INFLIGHT` tokio tasks that independently pump `send_msg_by_ip`. Reports the aggregate throughput ceiling the peer manager + tunnel can sustain across worker threads. |
|
|
||||||
|
|
||||||
> **Out of scope (by design):** TUN read/write (`no_tun = true`), compression
|
|
||||||
> (default `None`), reverse/RX-side measurement, multi-peer fanout. Add
|
|
||||||
> separate benchmarks if you need those.
|
|
||||||
|
|
||||||
## Quick start
|
|
||||||
|
|
||||||
### ring tunnel (no root, fastest)
|
|
||||||
|
|
||||||
```bash
|
|
||||||
cargo bench --bench tx_throughput
|
|
||||||
```
|
|
||||||
|
|
||||||
Smoke run (faster iteration):
|
|
||||||
|
|
||||||
```bash
|
|
||||||
TX_THROUGHPUT_MEASUREMENT_SECS=2 \
|
|
||||||
TX_THROUGHPUT_WARMUP_SECS=1 \
|
|
||||||
TX_THROUGHPUT_SAMPLE_SIZE=10 \
|
|
||||||
cargo bench --bench tx_throughput -- --quiet
|
|
||||||
```
|
|
||||||
|
|
||||||
### tcp / udp tunnels (requires Docker + root)
|
|
||||||
|
|
||||||
The benchmark creates a Docker network and registers each container's netns
|
|
||||||
under `/var/run/netns`, which requires root. Run the whole command under
|
|
||||||
`sudo`:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
sudo TX_THROUGHPUT_TUNNEL=tcp \
|
|
||||||
TX_THROUGHPUT_MEASUREMENT_SECS=5 \
|
|
||||||
TX_THROUGHPUT_WARMUP_SECS=2 \
|
|
||||||
TX_THROUGHPUT_INFLIGHT=64 \
|
|
||||||
cargo bench --bench tx_throughput -- --quiet
|
|
||||||
|
|
||||||
sudo TX_THROUGHPUT_TUNNEL=udp cargo bench --bench tx_throughput -- --quiet
|
|
||||||
```
|
|
||||||
|
|
||||||
> If `sudo` cannot find `cargo`, use `sudo -E` or the absolute path
|
|
||||||
> (`$(which cargo)`).
|
|
||||||
|
|
||||||
## Environment variables
|
|
||||||
|
|
||||||
| Variable | Default | Notes |
|
|
||||||
| -------------------------------- | --------------------- | -------------------------------------- |
|
|
||||||
| `TX_THROUGHPUT_TUNNEL` | `ring` | `ring` / `tcp` / `udp` |
|
|
||||||
| `TX_THROUGHPUT_PKT_SIZE` | `1400` | IP total length in bytes |
|
|
||||||
| `TX_THROUGHPUT_WORKER_THREADS` | `4` | tokio worker threads |
|
|
||||||
| `TX_THROUGHPUT_INFLIGHT` | `64` | saturate-mode concurrency (task count) |
|
|
||||||
| `TX_THROUGHPUT_TUNNEL_PORT` | `35521` | tcp/udp listen port |
|
|
||||||
| `TX_THROUGHPUT_MEASUREMENT_SECS` | `10` | Criterion `measurement_time` |
|
|
||||||
| `TX_THROUGHPUT_WARMUP_SECS` | `3` | Criterion `warm_up_time` |
|
|
||||||
| `TX_THROUGHPUT_SAMPLE_SIZE` | `10` | Criterion `sample_size` (min 10) |
|
|
||||||
| `TX_THROUGHPUT_DOCKER_IMAGE` | `busybox:latest` | tcp/udp only |
|
|
||||||
| `TX_THROUGHPUT_DOCKER_NET` | `easytier-bench-<id>` | auto-generated unique name |
|
|
||||||
| `TX_THROUGHPUT_DOCKER_SUBNET` | `172.31.250.0/24` | |
|
|
||||||
| `TX_THROUGHPUT_DOCKER_IP_A` | `172.31.250.2` | |
|
|
||||||
| `TX_THROUGHPUT_DOCKER_IP_B` | `172.31.250.3` | |
|
|
||||||
|
|
||||||
## Parameter sweeps
|
|
||||||
|
|
||||||
```bash
|
|
||||||
# Packet size
|
|
||||||
for sz in 64 256 1400 9000; do
|
|
||||||
TX_THROUGHPUT_PKT_SIZE=$sz cargo bench --bench tx_throughput -- --quick
|
|
||||||
done
|
|
||||||
|
|
||||||
# Inflight depth (self-check: depth=1 should match serial baseline)
|
|
||||||
for d in 1 4 16 64 256; do
|
|
||||||
TX_THROUGHPUT_INFLIGHT=$d cargo bench --bench tx_throughput -- --quick
|
|
||||||
done
|
|
||||||
|
|
||||||
# Worker threads
|
|
||||||
for w in 1 2 4 8; do
|
|
||||||
TX_THROUGHPUT_WORKER_THREADS=$w cargo bench --bench tx_throughput -- --quick
|
|
||||||
done
|
|
||||||
```
|
|
||||||
|
|
||||||
## Interpreting results
|
|
||||||
|
|
||||||
- **`<tunnel>`** reports per-packet latency. Lower is better. Throughput
|
|
||||||
column here is "what one in-flight sender sustains".
|
|
||||||
- **`<tunnel>-saturate`** reports aggregate throughput across
|
|
||||||
`TX_THROUGHPUT_INFLIGHT` concurrent senders. If this matches the serial
|
|
||||||
baseline, the TX path is bottlenecked on an internal serialization point
|
|
||||||
(lock, single-threaded queue, etc.) rather than CPU or link bandwidth.
|
|
||||||
|
|
||||||
### Known finding (ring, single peer)
|
|
||||||
|
|
||||||
On the ring tunnel with a single destination peer, saturate does **not** beat
|
|
||||||
serial (observed ~277 MiB/s saturate vs ~288 MiB/s serial on a 4-worker
|
|
||||||
runtime). This points to a serialization point inside the peer-connection TX
|
|
||||||
path. Tunnels with real I/O await points (tcp/udp via Docker) are expected to
|
|
||||||
show a saturate > serial gap; verify with the sudo commands above.
|
|
||||||
|
|
||||||
## Output artifacts
|
|
||||||
|
|
||||||
Criterion writes HTML reports + SVG plots under
|
|
||||||
`easytier/target/criterion/`. Open `tx_throughput/<tunnel>/report/index.html`
|
|
||||||
or `.../<tunnel>-saturate/report/index.html` in a browser to inspect
|
|
||||||
distributions and regressions across runs.
|
|
||||||
@@ -1,65 +0,0 @@
|
|||||||
use std::hint::black_box;
|
|
||||||
use std::time::Duration;
|
|
||||||
|
|
||||||
use criterion::{BatchSize, Criterion, Throughput, criterion_group, criterion_main};
|
|
||||||
|
|
||||||
use easytier::tunnel::packet_def::ZCPacket;
|
|
||||||
|
|
||||||
const PAYLOAD_SIZES: &[usize] = &[1280, 4096];
|
|
||||||
|
|
||||||
fn env_parse<T: std::str::FromStr>(key: &str, default: T) -> T {
|
|
||||||
std::env::var(key)
|
|
||||||
.ok()
|
|
||||||
.and_then(|v| v.parse().ok())
|
|
||||||
.unwrap_or(default)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn bench_payload_bytes(c: &mut Criterion) {
|
|
||||||
let mut group = c.benchmark_group("payload_bytes");
|
|
||||||
for &size in PAYLOAD_SIZES {
|
|
||||||
let data = vec![0u8; size];
|
|
||||||
group.throughput(Throughput::Bytes(size as u64));
|
|
||||||
group.bench_with_input(format!("{size}"), &data, |b, data| {
|
|
||||||
b.iter_batched(
|
|
||||||
|| ZCPacket::new_with_payload(black_box(data)),
|
|
||||||
|p| black_box(p).payload_bytes(),
|
|
||||||
BatchSize::SmallInput,
|
|
||||||
)
|
|
||||||
});
|
|
||||||
}
|
|
||||||
group.finish();
|
|
||||||
}
|
|
||||||
|
|
||||||
fn bench_tunnel_payload_bytes(c: &mut Criterion) {
|
|
||||||
let mut group = c.benchmark_group("tunnel_payload_bytes");
|
|
||||||
for &size in PAYLOAD_SIZES {
|
|
||||||
let data = vec![0u8; size];
|
|
||||||
group.throughput(Throughput::Bytes(size as u64));
|
|
||||||
group.bench_with_input(format!("{size}"), &data, |b, data| {
|
|
||||||
b.iter_batched(
|
|
||||||
|| ZCPacket::new_with_payload(black_box(data)),
|
|
||||||
|p| black_box(p).tunnel_payload_bytes(),
|
|
||||||
BatchSize::SmallInput,
|
|
||||||
)
|
|
||||||
});
|
|
||||||
}
|
|
||||||
group.finish();
|
|
||||||
}
|
|
||||||
|
|
||||||
fn criterion_config() -> Criterion {
|
|
||||||
let measurement_secs = env_parse("PACKET_BYTES_MEASUREMENT_SECS", 10u64);
|
|
||||||
let warmup_secs = env_parse("PACKET_BYTES_WARMUP_SECS", 3u64);
|
|
||||||
let sample_size = env_parse("PACKET_BYTES_SAMPLE_SIZE", 10usize).max(10);
|
|
||||||
|
|
||||||
Criterion::default()
|
|
||||||
.measurement_time(Duration::from_secs(measurement_secs))
|
|
||||||
.warm_up_time(Duration::from_secs(warmup_secs))
|
|
||||||
.sample_size(sample_size)
|
|
||||||
}
|
|
||||||
|
|
||||||
criterion_group! {
|
|
||||||
name = benches;
|
|
||||||
config = criterion_config();
|
|
||||||
targets = bench_payload_bytes, bench_tunnel_payload_bytes
|
|
||||||
}
|
|
||||||
criterion_main!(benches);
|
|
||||||
@@ -1,472 +0,0 @@
|
|||||||
use std::{
|
|
||||||
net::IpAddr,
|
|
||||||
path::PathBuf,
|
|
||||||
process::{Command, Stdio},
|
|
||||||
str::FromStr,
|
|
||||||
sync::Arc,
|
|
||||||
sync::atomic::{AtomicU64, Ordering},
|
|
||||||
time::{Duration, Instant, SystemTime, UNIX_EPOCH},
|
|
||||||
};
|
|
||||||
|
|
||||||
use bytes::BytesMut;
|
|
||||||
use criterion::{Criterion, Throughput, criterion_group, criterion_main};
|
|
||||||
|
|
||||||
use easytier::{
|
|
||||||
common::config::{ConfigLoader, TomlConfigLoader},
|
|
||||||
instance::instance::Instance,
|
|
||||||
tunnel::{
|
|
||||||
packet_def::ZCPacket, ring::RingTunnelConnector, tcp::TcpTunnelConnector,
|
|
||||||
udp::UdpTunnelConnector,
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
const VIRTUAL_IP_A: &str = "10.144.144.1";
|
|
||||||
const VIRTUAL_IP_B: &str = "10.144.144.2";
|
|
||||||
const DEFAULT_DOCKER_SUBNET: &str = "172.31.250.0/24";
|
|
||||||
const DEFAULT_DOCKER_IP_A: &str = "172.31.250.2";
|
|
||||||
const DEFAULT_DOCKER_IP_B: &str = "172.31.250.3";
|
|
||||||
const DEFAULT_TUNNEL_PORT: u16 = 35521;
|
|
||||||
|
|
||||||
#[derive(Clone, Copy, Debug)]
|
|
||||||
enum TunnelKind {
|
|
||||||
Ring,
|
|
||||||
Tcp,
|
|
||||||
Udp,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl TunnelKind {
|
|
||||||
fn as_str(self) -> &'static str {
|
|
||||||
match self {
|
|
||||||
TunnelKind::Ring => "ring",
|
|
||||||
TunnelKind::Tcp => "tcp",
|
|
||||||
TunnelKind::Udp => "udp",
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl FromStr for TunnelKind {
|
|
||||||
type Err = String;
|
|
||||||
|
|
||||||
fn from_str(value: &str) -> Result<Self, Self::Err> {
|
|
||||||
match value {
|
|
||||||
"ring" => Ok(TunnelKind::Ring),
|
|
||||||
"tcp" => Ok(TunnelKind::Tcp),
|
|
||||||
"udp" => Ok(TunnelKind::Udp),
|
|
||||||
other => Err(format!(
|
|
||||||
"unsupported TX_THROUGHPUT_TUNNEL={other:?}; expected ring, tcp, or udp"
|
|
||||||
)),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
struct BenchTopology {
|
|
||||||
_docker: Option<DockerNetns>,
|
|
||||||
inst_a: Instance,
|
|
||||||
_inst_b: Instance,
|
|
||||||
dst: IpAddr,
|
|
||||||
packet: ZCPacket,
|
|
||||||
}
|
|
||||||
|
|
||||||
struct DockerNetns {
|
|
||||||
network: String,
|
|
||||||
container_a: String,
|
|
||||||
container_b: String,
|
|
||||||
netns_a: String,
|
|
||||||
netns_b: String,
|
|
||||||
ip_a: String,
|
|
||||||
netns_a_path: PathBuf,
|
|
||||||
netns_b_path: PathBuf,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl DockerNetns {
|
|
||||||
fn create() -> Self {
|
|
||||||
let id = unique_id();
|
|
||||||
let image = env_string("TX_THROUGHPUT_DOCKER_IMAGE", "busybox:latest");
|
|
||||||
let network = env_string("TX_THROUGHPUT_DOCKER_NET", &format!("easytier-bench-{id}"));
|
|
||||||
let subnet = env_string("TX_THROUGHPUT_DOCKER_SUBNET", DEFAULT_DOCKER_SUBNET);
|
|
||||||
let ip_a = env_string("TX_THROUGHPUT_DOCKER_IP_A", DEFAULT_DOCKER_IP_A);
|
|
||||||
let ip_b = env_string("TX_THROUGHPUT_DOCKER_IP_B", DEFAULT_DOCKER_IP_B);
|
|
||||||
let container_a = format!("easytier-bench-a-{id}");
|
|
||||||
let container_b = format!("easytier-bench-b-{id}");
|
|
||||||
let netns_a = format!("easytier-bench-a-{id}");
|
|
||||||
let netns_b = format!("easytier-bench-b-{id}");
|
|
||||||
|
|
||||||
docker(&[
|
|
||||||
"network", "create", "--driver", "bridge", "--subnet", &subnet, &network,
|
|
||||||
]);
|
|
||||||
|
|
||||||
let mut docker_netns = Self {
|
|
||||||
network,
|
|
||||||
container_a,
|
|
||||||
container_b,
|
|
||||||
netns_a,
|
|
||||||
netns_b,
|
|
||||||
ip_a: ip_a.clone(),
|
|
||||||
netns_a_path: PathBuf::new(),
|
|
||||||
netns_b_path: PathBuf::new(),
|
|
||||||
};
|
|
||||||
|
|
||||||
docker_netns.start_container(&docker_netns.container_a, &ip_a, &image);
|
|
||||||
docker_netns.start_container(&docker_netns.container_b, &ip_b, &image);
|
|
||||||
|
|
||||||
let pid_a = docker(&["inspect", "-f", "{{.State.Pid}}", &docker_netns.container_a]);
|
|
||||||
let pid_b = docker(&["inspect", "-f", "{{.State.Pid}}", &docker_netns.container_b]);
|
|
||||||
|
|
||||||
docker_netns.netns_a_path = register_netns(&docker_netns.netns_a, &pid_a);
|
|
||||||
docker_netns.netns_b_path = register_netns(&docker_netns.netns_b, &pid_b);
|
|
||||||
docker_netns
|
|
||||||
}
|
|
||||||
|
|
||||||
fn start_container(&self, name: &str, ip: &str, image: &str) {
|
|
||||||
docker(&[
|
|
||||||
"run",
|
|
||||||
"-d",
|
|
||||||
"--name",
|
|
||||||
name,
|
|
||||||
"--network",
|
|
||||||
&self.network,
|
|
||||||
"--ip",
|
|
||||||
ip,
|
|
||||||
image,
|
|
||||||
"sleep",
|
|
||||||
"3600",
|
|
||||||
]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Drop for DockerNetns {
|
|
||||||
fn drop(&mut self) {
|
|
||||||
let _ = std::fs::remove_file(&self.netns_a_path);
|
|
||||||
let _ = std::fs::remove_file(&self.netns_b_path);
|
|
||||||
docker_ignore(&["rm", "-f", &self.container_a, &self.container_b]);
|
|
||||||
docker_ignore(&["network", "rm", &self.network]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn bench_tx_throughput(c: &mut Criterion) {
|
|
||||||
let tunnel = env_string("TX_THROUGHPUT_TUNNEL", "ring")
|
|
||||||
.parse::<TunnelKind>()
|
|
||||||
.unwrap_or_else(|err| panic!("{err}"));
|
|
||||||
let packet_size = env_parse("TX_THROUGHPUT_PKT_SIZE", 1400usize);
|
|
||||||
const MIN_PKT_SIZE: usize = 28; // IPv4 (20) + UDP (8) header
|
|
||||||
assert!(
|
|
||||||
packet_size >= MIN_PKT_SIZE,
|
|
||||||
"TX_THROUGHPUT_PKT_SIZE={packet_size} is smaller than the minimum {MIN_PKT_SIZE} (IPv4+UDP headers)"
|
|
||||||
);
|
|
||||||
let worker_threads = env_parse("TX_THROUGHPUT_WORKER_THREADS", 4usize);
|
|
||||||
let inflight_depth = env_parse("TX_THROUGHPUT_INFLIGHT", 64usize).max(1);
|
|
||||||
let runtime = tokio::runtime::Builder::new_multi_thread()
|
|
||||||
.worker_threads(worker_threads)
|
|
||||||
.enable_all()
|
|
||||||
.build()
|
|
||||||
.expect("create tokio runtime");
|
|
||||||
|
|
||||||
let topology = runtime.block_on(setup_topology(tunnel, packet_size));
|
|
||||||
let peer_manager = topology.inst_a.get_peer_manager();
|
|
||||||
let packet = topology.packet.clone();
|
|
||||||
let dst = topology.dst;
|
|
||||||
|
|
||||||
eprintln!(
|
|
||||||
"tx_throughput: tunnel={} inflight={} workers={} pkt_size={}",
|
|
||||||
tunnel.as_str(),
|
|
||||||
inflight_depth.max(1),
|
|
||||||
worker_threads,
|
|
||||||
packet_size
|
|
||||||
);
|
|
||||||
|
|
||||||
let mut group = c.benchmark_group("tx_throughput");
|
|
||||||
group.throughput(Throughput::Bytes(packet_size as u64));
|
|
||||||
|
|
||||||
// Serial baseline: one packet in flight at a time.
|
|
||||||
// Measures per-packet CPU cost (TX injection latency).
|
|
||||||
group.bench_function(tunnel.as_str(), |b| {
|
|
||||||
b.iter_custom(|iterations| {
|
|
||||||
let pm = peer_manager.clone();
|
|
||||||
let pkt = packet.clone();
|
|
||||||
runtime.block_on(async move {
|
|
||||||
let start = Instant::now();
|
|
||||||
for _ in 0..iterations {
|
|
||||||
pm.send_msg_by_ip(pkt.clone(), dst, false)
|
|
||||||
.await
|
|
||||||
.expect("send packet by EasyTier IP");
|
|
||||||
}
|
|
||||||
start.elapsed()
|
|
||||||
})
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
// Saturate: spawn TX_THROUGHPUT_INFLIGHT worker tasks, each independently
|
|
||||||
// pumping send_msg_by_ip. Work is distributed across tokio worker threads,
|
|
||||||
// exposing the peer manager + tunnel's true aggregate throughput ceiling.
|
|
||||||
// With TX_THROUGHPUT_INFLIGHT=1 it degrades to the serial baseline.
|
|
||||||
group.bench_function(format!("{}-saturate", tunnel.as_str()), |b| {
|
|
||||||
b.iter_custom(|iterations| {
|
|
||||||
let pm = peer_manager.clone();
|
|
||||||
let pkt = packet.clone();
|
|
||||||
let concurrency = inflight_depth.min(iterations as usize).max(1);
|
|
||||||
runtime.block_on(async move {
|
|
||||||
let counter = Arc::new(AtomicU64::new(iterations));
|
|
||||||
let start = Instant::now();
|
|
||||||
let mut handles = Vec::with_capacity(concurrency);
|
|
||||||
for _ in 0..concurrency {
|
|
||||||
let pm = pm.clone();
|
|
||||||
let pkt = pkt.clone();
|
|
||||||
let counter = counter.clone();
|
|
||||||
handles.push(tokio::spawn(async move {
|
|
||||||
loop {
|
|
||||||
if counter
|
|
||||||
.fetch_update(Ordering::AcqRel, Ordering::Acquire, |cur| {
|
|
||||||
if cur > 0 { Some(cur - 1) } else { None }
|
|
||||||
})
|
|
||||||
.is_err()
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
pm.send_msg_by_ip(pkt.clone(), dst, false)
|
|
||||||
.await
|
|
||||||
.expect("send packet by EasyTier IP");
|
|
||||||
}
|
|
||||||
}));
|
|
||||||
}
|
|
||||||
for h in handles {
|
|
||||||
h.await.expect("saturate worker task panicked");
|
|
||||||
}
|
|
||||||
start.elapsed()
|
|
||||||
})
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
group.finish();
|
|
||||||
|
|
||||||
runtime.block_on(async move {
|
|
||||||
drop(topology);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
async fn setup_topology(tunnel: TunnelKind, packet_size: usize) -> BenchTopology {
|
|
||||||
let tunnel_port = env_parse("TX_THROUGHPUT_TUNNEL_PORT", DEFAULT_TUNNEL_PORT);
|
|
||||||
let docker = match tunnel {
|
|
||||||
TunnelKind::Ring => None,
|
|
||||||
TunnelKind::Tcp | TunnelKind::Udp => Some(DockerNetns::create()),
|
|
||||||
};
|
|
||||||
|
|
||||||
let (netns_a, netns_b) = match &docker {
|
|
||||||
Some(docker) => (Some(docker.netns_a.clone()), Some(docker.netns_b.clone())),
|
|
||||||
None => (None, None),
|
|
||||||
};
|
|
||||||
let listeners_a = match tunnel {
|
|
||||||
TunnelKind::Ring => Vec::new(),
|
|
||||||
TunnelKind::Tcp | TunnelKind::Udp => vec![
|
|
||||||
format!("{}://0.0.0.0:{}", tunnel.as_str(), tunnel_port)
|
|
||||||
.parse()
|
|
||||||
.unwrap(),
|
|
||||||
],
|
|
||||||
};
|
|
||||||
|
|
||||||
let mut inst_a = Instance::new(no_tun_config("hot-a", VIRTUAL_IP_A, netns_a, listeners_a));
|
|
||||||
let mut inst_b = Instance::new(no_tun_config("hot-b", VIRTUAL_IP_B, netns_b, Vec::new()));
|
|
||||||
|
|
||||||
inst_a.run().await.expect("inst_a run");
|
|
||||||
inst_b.run().await.expect("inst_b run");
|
|
||||||
|
|
||||||
match tunnel {
|
|
||||||
TunnelKind::Ring => inst_b
|
|
||||||
.get_conn_manager()
|
|
||||||
.add_connector(RingTunnelConnector::new(
|
|
||||||
format!("ring://{}", inst_a.id()).parse().unwrap(),
|
|
||||||
)),
|
|
||||||
TunnelKind::Tcp => inst_b
|
|
||||||
.get_conn_manager()
|
|
||||||
.add_connector(TcpTunnelConnector::new(
|
|
||||||
format!(
|
|
||||||
"tcp://{}:{}",
|
|
||||||
docker.as_ref().expect("tcp benchmark needs Docker").ip_a,
|
|
||||||
tunnel_port
|
|
||||||
)
|
|
||||||
.parse()
|
|
||||||
.unwrap(),
|
|
||||||
)),
|
|
||||||
TunnelKind::Udp => inst_b
|
|
||||||
.get_conn_manager()
|
|
||||||
.add_connector(UdpTunnelConnector::new(
|
|
||||||
format!(
|
|
||||||
"udp://{}:{}",
|
|
||||||
docker.as_ref().expect("udp benchmark needs Docker").ip_a,
|
|
||||||
tunnel_port
|
|
||||||
)
|
|
||||||
.parse()
|
|
||||||
.unwrap(),
|
|
||||||
)),
|
|
||||||
}
|
|
||||||
|
|
||||||
wait_for_routes(&inst_a, &inst_b).await;
|
|
||||||
|
|
||||||
BenchTopology {
|
|
||||||
_docker: docker,
|
|
||||||
inst_a,
|
|
||||||
_inst_b: inst_b,
|
|
||||||
dst: VIRTUAL_IP_B.parse().unwrap(),
|
|
||||||
packet: make_data_packet(VIRTUAL_IP_A, VIRTUAL_IP_B, packet_size),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
async fn wait_for_routes(inst_a: &Instance, inst_b: &Instance) {
|
|
||||||
tokio::time::timeout(Duration::from_secs(15), async {
|
|
||||||
loop {
|
|
||||||
let routes_a = inst_a.get_peer_manager().list_routes().await;
|
|
||||||
let routes_b = inst_b.get_peer_manager().list_routes().await;
|
|
||||||
if !routes_a.is_empty() && !routes_b.is_empty() {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
tokio::time::sleep(Duration::from_millis(500)).await;
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.await
|
|
||||||
.expect("EasyTier routes did not converge within 15s");
|
|
||||||
}
|
|
||||||
|
|
||||||
fn make_data_packet(src: &str, dst: &str, total_size: usize) -> ZCPacket {
|
|
||||||
use std::net::Ipv4Addr;
|
|
||||||
|
|
||||||
let hdr_len = 28;
|
|
||||||
let payload_len = total_size.saturating_sub(hdr_len);
|
|
||||||
let ip_total_len = (hdr_len + payload_len) as u16;
|
|
||||||
let mut buf = BytesMut::with_capacity(total_size);
|
|
||||||
|
|
||||||
buf.extend_from_slice(&[
|
|
||||||
0x45,
|
|
||||||
0x00,
|
|
||||||
(ip_total_len >> 8) as u8,
|
|
||||||
(ip_total_len & 0xff) as u8,
|
|
||||||
0x00,
|
|
||||||
0x00,
|
|
||||||
0x40,
|
|
||||||
0x00,
|
|
||||||
0x40,
|
|
||||||
0x11,
|
|
||||||
0x00,
|
|
||||||
0x00,
|
|
||||||
]);
|
|
||||||
let src: Ipv4Addr = src.parse().unwrap();
|
|
||||||
buf.extend_from_slice(&src.octets());
|
|
||||||
let dst: Ipv4Addr = dst.parse().unwrap();
|
|
||||||
buf.extend_from_slice(&dst.octets());
|
|
||||||
|
|
||||||
let udp_len = (8 + payload_len) as u16;
|
|
||||||
buf.extend_from_slice(&[
|
|
||||||
0x30,
|
|
||||||
0x39,
|
|
||||||
0xd4,
|
|
||||||
0x31,
|
|
||||||
(udp_len >> 8) as u8,
|
|
||||||
(udp_len & 0xff) as u8,
|
|
||||||
0x00,
|
|
||||||
0x00,
|
|
||||||
]);
|
|
||||||
|
|
||||||
buf.resize(total_size, 0xaa);
|
|
||||||
ZCPacket::new_with_payload(&buf)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn no_tun_config(
|
|
||||||
name: &str,
|
|
||||||
ipv4: &str,
|
|
||||||
netns: Option<String>,
|
|
||||||
listeners: Vec<url::Url>,
|
|
||||||
) -> TomlConfigLoader {
|
|
||||||
let config = TomlConfigLoader::default();
|
|
||||||
config.set_inst_name(name.to_owned());
|
|
||||||
config.set_netns(netns);
|
|
||||||
config.set_ipv4(Some(ipv4.parse().unwrap()));
|
|
||||||
config.set_listeners(listeners);
|
|
||||||
let mut flags = config.get_flags();
|
|
||||||
flags.no_tun = true;
|
|
||||||
config.set_flags(flags);
|
|
||||||
config
|
|
||||||
}
|
|
||||||
|
|
||||||
fn register_netns(name: &str, pid: &str) -> PathBuf {
|
|
||||||
#[cfg(target_os = "linux")]
|
|
||||||
{
|
|
||||||
let dir = PathBuf::from("/var/run/netns");
|
|
||||||
std::fs::create_dir_all(&dir).expect("create /var/run/netns");
|
|
||||||
let path = dir.join(name);
|
|
||||||
let _ = std::fs::remove_file(&path);
|
|
||||||
std::os::unix::fs::symlink(format!("/proc/{pid}/ns/net"), &path)
|
|
||||||
.expect("link Docker netns into /var/run/netns");
|
|
||||||
path
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(not(target_os = "linux"))]
|
|
||||||
{
|
|
||||||
let _ = (name, pid);
|
|
||||||
panic!("Docker netns benchmark requires Linux");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn docker(args: &[&str]) -> String {
|
|
||||||
let output = Command::new("docker")
|
|
||||||
.args(args)
|
|
||||||
.output()
|
|
||||||
.unwrap_or_else(|err| panic!("failed to run docker {args:?}: {err}"));
|
|
||||||
if !output.status.success() {
|
|
||||||
panic!(
|
|
||||||
"docker {:?} failed with status {:?}: {}",
|
|
||||||
args,
|
|
||||||
output.status.code(),
|
|
||||||
String::from_utf8_lossy(&output.stderr)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
String::from_utf8_lossy(&output.stdout).trim().to_owned()
|
|
||||||
}
|
|
||||||
|
|
||||||
fn docker_ignore(args: &[&str]) {
|
|
||||||
let _ = Command::new("docker")
|
|
||||||
.args(args)
|
|
||||||
.stdout(Stdio::null())
|
|
||||||
.stderr(Stdio::null())
|
|
||||||
.status();
|
|
||||||
}
|
|
||||||
|
|
||||||
fn env_string(name: &str, default: &str) -> String {
|
|
||||||
std::env::var(name).unwrap_or_else(|_| default.to_owned())
|
|
||||||
}
|
|
||||||
|
|
||||||
fn env_parse<T>(name: &str, default: T) -> T
|
|
||||||
where
|
|
||||||
T: FromStr,
|
|
||||||
T::Err: std::fmt::Display,
|
|
||||||
{
|
|
||||||
match std::env::var(name) {
|
|
||||||
Ok(value) => value
|
|
||||||
.parse()
|
|
||||||
.unwrap_or_else(|err| panic!("invalid {name}={value:?}: {err}")),
|
|
||||||
Err(_) => default,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn criterion_config() -> Criterion {
|
|
||||||
let measurement_secs = env_parse("TX_THROUGHPUT_MEASUREMENT_SECS", 10u64);
|
|
||||||
let warmup_secs = env_parse("TX_THROUGHPUT_WARMUP_SECS", 3u64);
|
|
||||||
let sample_size = env_parse("TX_THROUGHPUT_SAMPLE_SIZE", 10usize).max(10);
|
|
||||||
|
|
||||||
Criterion::default()
|
|
||||||
.measurement_time(Duration::from_secs(measurement_secs))
|
|
||||||
.warm_up_time(Duration::from_secs(warmup_secs))
|
|
||||||
.sample_size(sample_size)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn unique_id() -> String {
|
|
||||||
let nanos = SystemTime::now()
|
|
||||||
.duration_since(UNIX_EPOCH)
|
|
||||||
.expect("system clock before UNIX epoch")
|
|
||||||
.as_nanos();
|
|
||||||
format!("{}-{nanos}", std::process::id())
|
|
||||||
}
|
|
||||||
|
|
||||||
criterion_group! {
|
|
||||||
name = benches;
|
|
||||||
config = criterion_config();
|
|
||||||
targets = bench_tx_throughput
|
|
||||||
}
|
|
||||||
criterion_main!(benches);
|
|
||||||
@@ -133,6 +133,35 @@ fn check_locale() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "lttng")]
|
||||||
|
fn generate_lttng_tracepoints(out: &PathBuf) -> Result<(), Box<dyn std::error::Error>> {
|
||||||
|
use lttng_ust_generate::{CIntegerType, CTFType, Generator, LogLevel, Provider};
|
||||||
|
|
||||||
|
let mut provider = Provider::new("easytier");
|
||||||
|
provider
|
||||||
|
.create_class("marker")
|
||||||
|
.add_field("name", CTFType::SequenceText)
|
||||||
|
.add_field("value", CTFType::Integer(CIntegerType::U64))
|
||||||
|
.instantiate_with_level("mark", LogLevel::Info);
|
||||||
|
|
||||||
|
provider
|
||||||
|
.create_class("duration")
|
||||||
|
.add_field("name", CTFType::SequenceText)
|
||||||
|
.add_field("duration_micros", CTFType::Integer(CIntegerType::U64))
|
||||||
|
.instantiate_with_level("duration_micros", LogLevel::Info);
|
||||||
|
|
||||||
|
Generator::default()
|
||||||
|
.generated_lib_name("easytier_lttng_tracepoints")
|
||||||
|
.register_provider(provider)
|
||||||
|
.output_file_name(out.join("lttng_tracepoints.rs"))
|
||||||
|
.generate()
|
||||||
|
.map_err(|error| {
|
||||||
|
std::io::Error::other(format!("failed to generate LTTng tracepoints: {error:?}"))
|
||||||
|
})?;
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
fn main() -> Result<(), Box<dyn std::error::Error>> {
|
fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||||
cfg_aliases! {
|
cfg_aliases! {
|
||||||
mobile: {
|
mobile: {
|
||||||
@@ -174,6 +203,9 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
let out = PathBuf::from(env::var("OUT_DIR")?);
|
let out = PathBuf::from(env::var("OUT_DIR")?);
|
||||||
|
#[cfg(feature = "lttng")]
|
||||||
|
generate_lttng_tracepoints(&out)?;
|
||||||
|
|
||||||
let descriptor = out.join("descriptors.bin");
|
let descriptor = out.join("descriptors.bin");
|
||||||
|
|
||||||
let mut config = prost_build::Config::new();
|
let mut config = prost_build::Config::new();
|
||||||
|
|||||||
@@ -219,7 +219,6 @@ pub struct GlobalCtx {
|
|||||||
|
|
||||||
running_listeners: Mutex<Vec<url::Url>>,
|
running_listeners: Mutex<Vec<url::Url>>,
|
||||||
advertised_ipv6_public_addr_prefix: Mutex<Option<cidr::Ipv6Cidr>>,
|
advertised_ipv6_public_addr_prefix: Mutex<Option<cidr::Ipv6Cidr>>,
|
||||||
tun_device_name: Mutex<Option<String>>,
|
|
||||||
|
|
||||||
flags: ArcSwap<Flags>,
|
flags: ArcSwap<Flags>,
|
||||||
|
|
||||||
@@ -337,7 +336,6 @@ impl GlobalCtx {
|
|||||||
|
|
||||||
running_listeners: Mutex::new(Vec::new()),
|
running_listeners: Mutex::new(Vec::new()),
|
||||||
advertised_ipv6_public_addr_prefix: Mutex::new(None),
|
advertised_ipv6_public_addr_prefix: Mutex::new(None),
|
||||||
tun_device_name: Mutex::new(None),
|
|
||||||
|
|
||||||
flags: ArcSwap::new(Arc::new(flags)),
|
flags: ArcSwap::new(Arc::new(flags)),
|
||||||
|
|
||||||
@@ -372,24 +370,6 @@ impl GlobalCtx {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn set_tun_device_name(&self, name: Option<String>) {
|
|
||||||
*self.tun_device_name.lock().unwrap() = name;
|
|
||||||
}
|
|
||||||
|
|
||||||
pub(crate) fn set_tun_device_ready(&self, name: String) {
|
|
||||||
self.set_tun_device_name(Some(name.clone()));
|
|
||||||
self.issue_event(GlobalCtxEvent::TunDeviceReady(name));
|
|
||||||
}
|
|
||||||
|
|
||||||
pub(crate) fn set_tun_device_error(&self, error: String) {
|
|
||||||
self.set_tun_device_name(None);
|
|
||||||
self.issue_event(GlobalCtxEvent::TunDeviceError(error));
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn get_tun_device_name(&self) -> Option<String> {
|
|
||||||
self.tun_device_name.lock().unwrap().clone()
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn check_network_in_whitelist(&self, network_name: &str) -> Result<(), anyhow::Error> {
|
pub fn check_network_in_whitelist(&self, network_name: &str) -> Result<(), anyhow::Error> {
|
||||||
if self
|
if self
|
||||||
.get_flags()
|
.get_flags()
|
||||||
@@ -845,36 +825,6 @@ pub mod tests {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[tokio::test]
|
|
||||||
async fn test_tun_device_name_tracks_explicit_runtime_state() {
|
|
||||||
let config = TomlConfigLoader::default();
|
|
||||||
let global_ctx = GlobalCtx::new(config);
|
|
||||||
|
|
||||||
assert_eq!(global_ctx.get_tun_device_name(), None);
|
|
||||||
|
|
||||||
global_ctx.issue_event(GlobalCtxEvent::TunDeviceReady("ignored".to_string()));
|
|
||||||
assert_eq!(global_ctx.get_tun_device_name(), None);
|
|
||||||
|
|
||||||
let mut subscriber = global_ctx.subscribe();
|
|
||||||
|
|
||||||
global_ctx.set_tun_device_ready("easytier0".to_string());
|
|
||||||
assert_eq!(
|
|
||||||
global_ctx.get_tun_device_name(),
|
|
||||||
Some("easytier0".to_string())
|
|
||||||
);
|
|
||||||
assert_eq!(
|
|
||||||
subscriber.recv().await.unwrap(),
|
|
||||||
GlobalCtxEvent::TunDeviceReady("easytier0".to_string())
|
|
||||||
);
|
|
||||||
|
|
||||||
global_ctx.set_tun_device_error("closed".to_string());
|
|
||||||
assert_eq!(global_ctx.get_tun_device_name(), None);
|
|
||||||
assert_eq!(
|
|
||||||
subscriber.recv().await.unwrap(),
|
|
||||||
GlobalCtxEvent::TunDeviceError("closed".to_string())
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[tokio::test]
|
#[tokio::test]
|
||||||
async fn trusted_key_source_lookup_is_precise() {
|
async fn trusted_key_source_lookup_is_precise() {
|
||||||
let config = TomlConfigLoader::default();
|
let config = TomlConfigLoader::default();
|
||||||
|
|||||||
@@ -177,20 +177,3 @@ pub(crate) fn list_ipv6_route_messages()
|
|||||||
pub(crate) fn get_interface_index(name: &str) -> Result<u32, Error> {
|
pub(crate) fn get_interface_index(name: &str) -> Result<u32, Error> {
|
||||||
netlink::NetlinkIfConfiger::get_interface_index(name)
|
netlink::NetlinkIfConfiger::get_interface_index(name)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(target_os = "linux")]
|
|
||||||
pub(crate) fn add_ipv6_ndp_proxy(name: &str, address: Ipv6Addr) -> Result<(), Error> {
|
|
||||||
netlink::NetlinkIfConfiger::add_ipv6_ndp_proxy(name, address)
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(target_os = "linux")]
|
|
||||||
pub(crate) fn remove_ipv6_ndp_proxy(name: &str, address: Ipv6Addr) -> Result<(), Error> {
|
|
||||||
netlink::NetlinkIfConfiger::remove_ipv6_ndp_proxy(name, address)
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(target_os = "linux")]
|
|
||||||
pub(crate) fn list_ipv6_ndp_proxy(
|
|
||||||
name: &str,
|
|
||||||
) -> Result<std::collections::BTreeSet<Ipv6Addr>, Error> {
|
|
||||||
netlink::NetlinkIfConfiger::list_ipv6_ndp_proxy(name)
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
use std::{
|
use std::{
|
||||||
collections::BTreeSet,
|
|
||||||
ffi::CString,
|
ffi::CString,
|
||||||
fmt::Debug,
|
fmt::Debug,
|
||||||
net::{IpAddr, Ipv4Addr, Ipv6Addr},
|
net::{IpAddr, Ipv4Addr, Ipv6Addr},
|
||||||
@@ -17,10 +16,6 @@ use netlink_packet_core::{
|
|||||||
use netlink_packet_route::{
|
use netlink_packet_route::{
|
||||||
AddressFamily, RouteNetlinkMessage,
|
AddressFamily, RouteNetlinkMessage,
|
||||||
address::{AddressAttribute, AddressMessage},
|
address::{AddressAttribute, AddressMessage},
|
||||||
neighbour::{
|
|
||||||
NeighbourAddress, NeighbourAttribute, NeighbourFlags, NeighbourHeader, NeighbourMessage,
|
|
||||||
NeighbourState,
|
|
||||||
},
|
|
||||||
route::{
|
route::{
|
||||||
RouteAddress, RouteAttribute, RouteHeader, RouteMessage, RouteProtocol, RouteScope,
|
RouteAddress, RouteAttribute, RouteHeader, RouteMessage, RouteProtocol, RouteScope,
|
||||||
RouteType,
|
RouteType,
|
||||||
@@ -380,105 +375,6 @@ impl NetlinkIfConfiger {
|
|||||||
pub(crate) fn list_ipv6_route_messages() -> Result<Vec<RouteMessage>, Error> {
|
pub(crate) fn list_ipv6_route_messages() -> Result<Vec<RouteMessage>, Error> {
|
||||||
Self::list_route_messages(AddressFamily::Inet6)
|
Self::list_route_messages(AddressFamily::Inet6)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn ipv6_ndp_proxy_message(name: &str, address: Ipv6Addr) -> Result<NeighbourMessage, Error> {
|
|
||||||
let mut message = NeighbourMessage::default();
|
|
||||||
message.header = NeighbourHeader {
|
|
||||||
family: AddressFamily::Inet6,
|
|
||||||
ifindex: Self::get_interface_index(name)?,
|
|
||||||
state: NeighbourState::Permanent,
|
|
||||||
flags: NeighbourFlags::Proxy,
|
|
||||||
kind: RouteType::Unicast,
|
|
||||||
};
|
|
||||||
message
|
|
||||||
.attributes
|
|
||||||
.push(NeighbourAttribute::Destination(NeighbourAddress::Inet6(
|
|
||||||
address,
|
|
||||||
)));
|
|
||||||
Ok(message)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub(crate) fn add_ipv6_ndp_proxy(name: &str, address: Ipv6Addr) -> Result<(), Error> {
|
|
||||||
send_netlink_req_and_wait_one_resp(
|
|
||||||
RouteNetlinkMessage::NewNeighbour(Self::ipv6_ndp_proxy_message(name, address)?),
|
|
||||||
false,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub(crate) fn remove_ipv6_ndp_proxy(name: &str, address: Ipv6Addr) -> Result<(), Error> {
|
|
||||||
send_netlink_req_and_wait_one_resp(
|
|
||||||
RouteNetlinkMessage::DelNeighbour(Self::ipv6_ndp_proxy_message(name, address)?),
|
|
||||||
true,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn list_neighbour_messages(
|
|
||||||
address_family: AddressFamily,
|
|
||||||
) -> Result<Vec<NeighbourMessage>, Error> {
|
|
||||||
let mut message = NeighbourMessage::default();
|
|
||||||
message.header.family = address_family;
|
|
||||||
message.header.flags = NeighbourFlags::Proxy;
|
|
||||||
|
|
||||||
let s = send_netlink_req(
|
|
||||||
RouteNetlinkMessage::GetNeighbour(message),
|
|
||||||
NLM_F_REQUEST | NLM_F_DUMP,
|
|
||||||
)?;
|
|
||||||
|
|
||||||
let mut ret_vec = vec![];
|
|
||||||
let mut resp = Vec::<u8>::new();
|
|
||||||
loop {
|
|
||||||
if resp.is_empty() {
|
|
||||||
let (new_resp, _) = s.recv_from_full()?;
|
|
||||||
resp = new_resp;
|
|
||||||
}
|
|
||||||
|
|
||||||
let ret = NetlinkMessage::<RouteNetlinkMessage>::deserialize(&resp)
|
|
||||||
.with_context(|| "Failed to deserialize netlink neighbour message")?;
|
|
||||||
resp = resp.split_off(ret.buffer_len());
|
|
||||||
|
|
||||||
tracing::debug!("net link response <<< {:?}", ret);
|
|
||||||
|
|
||||||
match ret.payload {
|
|
||||||
NetlinkPayload::Error(e) => {
|
|
||||||
if e.code == NonZero::new(0) {
|
|
||||||
continue;
|
|
||||||
} else {
|
|
||||||
return Err(e.to_io().into());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
NetlinkPayload::InnerMessage(RouteNetlinkMessage::NewNeighbour(m)) => {
|
|
||||||
ret_vec.push(m);
|
|
||||||
}
|
|
||||||
NetlinkPayload::Done(_) => {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
p => {
|
|
||||||
tracing::error!("Unexpected netlink response: {:?}", p);
|
|
||||||
return Err(anyhow::anyhow!("Unexpected netlink response").into());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(ret_vec)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub(crate) fn list_ipv6_ndp_proxy(name: &str) -> Result<BTreeSet<Ipv6Addr>, Error> {
|
|
||||||
let ifindex = Self::get_interface_index(name)?;
|
|
||||||
|
|
||||||
Ok(Self::list_neighbour_messages(AddressFamily::Inet6)?
|
|
||||||
.into_iter()
|
|
||||||
.filter(|message| {
|
|
||||||
message.header.ifindex == ifindex
|
|
||||||
&& message.header.flags.contains(NeighbourFlags::Proxy)
|
|
||||||
})
|
|
||||||
.filter_map(|message| {
|
|
||||||
message.attributes.into_iter().find_map(|attr| match attr {
|
|
||||||
NeighbourAttribute::Destination(NeighbourAddress::Inet6(addr)) => Some(addr),
|
|
||||||
_ => None,
|
|
||||||
})
|
|
||||||
})
|
|
||||||
.collect())
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[async_trait]
|
#[async_trait]
|
||||||
|
|||||||
@@ -0,0 +1,23 @@
|
|||||||
|
#[cfg(feature = "lttng")]
|
||||||
|
lttng_ust::import_tracepoints!(
|
||||||
|
concat!(env!("OUT_DIR"), "/lttng_tracepoints.rs"),
|
||||||
|
tracepoints
|
||||||
|
);
|
||||||
|
|
||||||
|
#[cfg(feature = "lttng")]
|
||||||
|
pub fn mark(name: &str, value: u64) {
|
||||||
|
tracepoints::easytier::mark(name, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(not(feature = "lttng"))]
|
||||||
|
#[inline(always)]
|
||||||
|
pub fn mark(_name: &str, _value: u64) {}
|
||||||
|
|
||||||
|
#[cfg(feature = "lttng")]
|
||||||
|
pub fn duration_micros(name: &str, duration_micros: u64) {
|
||||||
|
tracepoints::easytier::duration_micros(name, duration_micros);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(not(feature = "lttng"))]
|
||||||
|
#[inline(always)]
|
||||||
|
pub fn duration_micros(_name: &str, _duration_micros: u64) {}
|
||||||
@@ -18,6 +18,7 @@ pub mod global_ctx;
|
|||||||
pub mod idn;
|
pub mod idn;
|
||||||
pub mod ifcfg;
|
pub mod ifcfg;
|
||||||
pub mod log;
|
pub mod log;
|
||||||
|
pub mod lttng;
|
||||||
pub mod machine_id;
|
pub mod machine_id;
|
||||||
pub mod netns;
|
pub mod netns;
|
||||||
pub mod network;
|
pub mod network;
|
||||||
|
|||||||
@@ -65,9 +65,9 @@ use crate::vpn_portal::{self, VpnPortal};
|
|||||||
use super::dns_server::{MAGIC_DNS_FAKE_IP, runner::DnsRunner};
|
use super::dns_server::{MAGIC_DNS_FAKE_IP, runner::DnsRunner};
|
||||||
use super::listeners::ListenerManager;
|
use super::listeners::ListenerManager;
|
||||||
use super::public_ipv6_provider::{
|
use super::public_ipv6_provider::{
|
||||||
PublicIpv6ProviderReconcileTask, reconcile_public_ipv6_provider_runtime,
|
reconcile_public_ipv6_provider_runtime, run_public_ipv6_provider_reconcile_task,
|
||||||
run_public_ipv6_provider_reconcile_task, should_run_public_ipv6_provider_reconcile,
|
should_run_public_ipv6_provider_reconcile, validate_public_ipv6_config,
|
||||||
validate_public_ipv6_config, validate_public_ipv6_config_values,
|
validate_public_ipv6_config_values,
|
||||||
};
|
};
|
||||||
|
|
||||||
#[cfg(feature = "socks5")]
|
#[cfg(feature = "socks5")]
|
||||||
@@ -194,44 +194,6 @@ impl NicCtxContainer {
|
|||||||
|
|
||||||
#[cfg(feature = "tun")]
|
#[cfg(feature = "tun")]
|
||||||
type ArcNicCtx = Arc<Mutex<Option<NicCtxContainer>>>;
|
type ArcNicCtx = Arc<Mutex<Option<NicCtxContainer>>>;
|
||||||
type ArcPublicIpv6ProviderTaskSlot = Arc<PublicIpv6ProviderTaskSlot>;
|
|
||||||
|
|
||||||
struct PublicIpv6ProviderTaskSlot {
|
|
||||||
task: Mutex<Option<PublicIpv6ProviderReconcileTask>>,
|
|
||||||
closing: AtomicBool,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl PublicIpv6ProviderTaskSlot {
|
|
||||||
fn new() -> Self {
|
|
||||||
Self {
|
|
||||||
task: Mutex::new(None),
|
|
||||||
closing: AtomicBool::new(false),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
async fn ensure_started(&self, global_ctx: &ArcGlobalCtx) {
|
|
||||||
let mut task = self.task.lock().await;
|
|
||||||
if self.closing.load(Ordering::Acquire) || task.is_some() {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
*task = run_public_ipv6_provider_reconcile_task(global_ctx);
|
|
||||||
}
|
|
||||||
|
|
||||||
async fn shutdown(&self) {
|
|
||||||
self.closing.store(true, Ordering::Release);
|
|
||||||
let task = self.task.lock().await.take();
|
|
||||||
if let Some(task) = task {
|
|
||||||
task.shutdown().await;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
async fn ensure_public_ipv6_provider_reconcile_task(
|
|
||||||
global_ctx: &ArcGlobalCtx,
|
|
||||||
task_slot: &ArcPublicIpv6ProviderTaskSlot,
|
|
||||||
) {
|
|
||||||
task_slot.ensure_started(global_ctx).await;
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct InstanceRpcServerHook {
|
pub struct InstanceRpcServerHook {
|
||||||
rpc_portal_whitelist: Vec<IpCidr>,
|
rpc_portal_whitelist: Vec<IpCidr>,
|
||||||
@@ -292,7 +254,6 @@ pub struct InstanceConfigPatcher {
|
|||||||
socks5_server: Weak<Socks5Server>,
|
socks5_server: Weak<Socks5Server>,
|
||||||
peer_manager: Weak<PeerManager>,
|
peer_manager: Weak<PeerManager>,
|
||||||
conn_manager: Weak<ManualConnectorManager>,
|
conn_manager: Weak<ManualConnectorManager>,
|
||||||
public_ipv6_provider_task: ArcPublicIpv6ProviderTaskSlot,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl InstanceConfigPatcher {
|
impl InstanceConfigPatcher {
|
||||||
@@ -363,6 +324,7 @@ impl InstanceConfigPatcher {
|
|||||||
self.patch_mapped_listeners(patch.mapped_listeners).await?;
|
self.patch_mapped_listeners(patch.mapped_listeners).await?;
|
||||||
self.patch_connector(patch.connectors).await?;
|
self.patch_connector(patch.connectors).await?;
|
||||||
|
|
||||||
|
let provider_reconcile_was_running = should_run_public_ipv6_provider_reconcile(&global_ctx);
|
||||||
let mut provider_config_changed = false;
|
let mut provider_config_changed = false;
|
||||||
if let Some(hostname) = patch.hostname {
|
if let Some(hostname) = patch.hostname {
|
||||||
global_ctx.set_hostname(hostname.clone());
|
global_ctx.set_hostname(hostname.clone());
|
||||||
@@ -400,12 +362,10 @@ impl InstanceConfigPatcher {
|
|||||||
if provider_config_changed {
|
if provider_config_changed {
|
||||||
reconcile_public_ipv6_provider_runtime(&global_ctx).await;
|
reconcile_public_ipv6_provider_runtime(&global_ctx).await;
|
||||||
|
|
||||||
if should_run_public_ipv6_provider_reconcile(&global_ctx) {
|
let provider_reconcile_should_run =
|
||||||
ensure_public_ipv6_provider_reconcile_task(
|
should_run_public_ipv6_provider_reconcile(&global_ctx);
|
||||||
&global_ctx,
|
if !provider_reconcile_was_running && provider_reconcile_should_run {
|
||||||
&self.public_ipv6_provider_task,
|
run_public_ipv6_provider_reconcile_task(&global_ctx);
|
||||||
)
|
|
||||||
.await;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -687,7 +647,6 @@ pub struct Instance {
|
|||||||
socks5_server: Arc<Socks5Server>,
|
socks5_server: Arc<Socks5Server>,
|
||||||
|
|
||||||
proxy_cidrs_monitor: Option<AbortOnDropHandle<()>>,
|
proxy_cidrs_monitor: Option<AbortOnDropHandle<()>>,
|
||||||
public_ipv6_provider_task: ArcPublicIpv6ProviderTaskSlot,
|
|
||||||
|
|
||||||
global_ctx: ArcGlobalCtx,
|
global_ctx: ArcGlobalCtx,
|
||||||
}
|
}
|
||||||
@@ -775,7 +734,6 @@ impl Instance {
|
|||||||
socks5_server,
|
socks5_server,
|
||||||
|
|
||||||
proxy_cidrs_monitor: None,
|
proxy_cidrs_monitor: None,
|
||||||
public_ipv6_provider_task: Arc::new(PublicIpv6ProviderTaskSlot::new()),
|
|
||||||
|
|
||||||
global_ctx,
|
global_ctx,
|
||||||
}
|
}
|
||||||
@@ -1076,11 +1034,7 @@ impl Instance {
|
|||||||
.await?;
|
.await?;
|
||||||
self.listener_manager.lock().await.run().await?;
|
self.listener_manager.lock().await.run().await?;
|
||||||
self.peer_manager.run().await?;
|
self.peer_manager.run().await?;
|
||||||
ensure_public_ipv6_provider_reconcile_task(
|
run_public_ipv6_provider_reconcile_task(&self.global_ctx);
|
||||||
&self.global_ctx,
|
|
||||||
&self.public_ipv6_provider_task,
|
|
||||||
)
|
|
||||||
.await;
|
|
||||||
|
|
||||||
#[cfg(feature = "tun")]
|
#[cfg(feature = "tun")]
|
||||||
{
|
{
|
||||||
@@ -1393,7 +1347,6 @@ impl Instance {
|
|||||||
socks5_server: Arc::downgrade(&self.socks5_server),
|
socks5_server: Arc::downgrade(&self.socks5_server),
|
||||||
peer_manager: Arc::downgrade(&self.peer_manager),
|
peer_manager: Arc::downgrade(&self.peer_manager),
|
||||||
conn_manager: Arc::downgrade(&self.conn_manager),
|
conn_manager: Arc::downgrade(&self.conn_manager),
|
||||||
public_ipv6_provider_task: self.public_ipv6_provider_task.clone(),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1649,7 +1602,6 @@ impl Instance {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub async fn clear_resources(&mut self) {
|
pub async fn clear_resources(&mut self) {
|
||||||
self.public_ipv6_provider_task.shutdown().await;
|
|
||||||
self.peer_manager.clear_resources().await;
|
self.peer_manager.clear_resources().await;
|
||||||
#[cfg(feature = "tun")]
|
#[cfg(feature = "tun")]
|
||||||
let _ = self.nic_ctx.lock().await.take();
|
let _ = self.nic_ctx.lock().await.take();
|
||||||
@@ -1835,21 +1787,6 @@ mod tests {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[tokio::test]
|
|
||||||
async fn public_ipv6_provider_task_slot_does_not_restart_after_shutdown() {
|
|
||||||
let global_ctx = get_mock_global_ctx();
|
|
||||||
let slot = std::sync::Arc::new(super::PublicIpv6ProviderTaskSlot::new());
|
|
||||||
global_ctx.config.set_ipv6_public_addr_provider(true);
|
|
||||||
global_ctx
|
|
||||||
.config
|
|
||||||
.set_ipv6_public_addr_prefix(Some("2001:db8::/48".parse().unwrap()));
|
|
||||||
|
|
||||||
slot.shutdown().await;
|
|
||||||
super::ensure_public_ipv6_provider_reconcile_task(&global_ctx, &slot).await;
|
|
||||||
|
|
||||||
assert!(slot.task.lock().await.is_none());
|
|
||||||
}
|
|
||||||
|
|
||||||
#[tokio::test]
|
#[tokio::test]
|
||||||
async fn validate_public_ipv6_patch_allows_enabling_auto_with_manual_ipv6() {
|
async fn validate_public_ipv6_patch_allows_enabling_auto_with_manual_ipv6() {
|
||||||
let global_ctx = get_mock_global_ctx();
|
let global_ctx = get_mock_global_ctx();
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -24,7 +24,7 @@ use crate::{
|
|||||||
};
|
};
|
||||||
|
|
||||||
use byteorder::WriteBytesExt as _;
|
use byteorder::WriteBytesExt as _;
|
||||||
use bytes::{Buf, BufMut, BytesMut};
|
use bytes::{BufMut, BytesMut};
|
||||||
use cidr::{Ipv4Inet, Ipv6Inet};
|
use cidr::{Ipv4Inet, Ipv6Inet};
|
||||||
use futures::{SinkExt, Stream, StreamExt, lock::BiLock, ready};
|
use futures::{SinkExt, Stream, StreamExt, lock::BiLock, ready};
|
||||||
use pin_project_lite::pin_project;
|
use pin_project_lite::pin_project;
|
||||||
@@ -180,13 +180,12 @@ impl ZCPacketToBytes for TunZCPacketToBytes {
|
|||||||
assert!(payload_offset >= 4);
|
assert!(payload_offset >= 4);
|
||||||
|
|
||||||
let ret = if self.has_packet_info {
|
let ret = if self.has_packet_info {
|
||||||
inner.advance(payload_offset - 4);
|
let mut inner = inner.split_off(payload_offset - 4);
|
||||||
let proto = infer_proto(&inner[4..]);
|
let proto = infer_proto(&inner[4..]);
|
||||||
self.fill_packet_info(&mut inner[0..4], proto)?;
|
self.fill_packet_info(&mut inner[0..4], proto)?;
|
||||||
inner
|
inner
|
||||||
} else {
|
} else {
|
||||||
inner.advance(payload_offset);
|
inner.split_off(payload_offset)
|
||||||
inner
|
|
||||||
};
|
};
|
||||||
|
|
||||||
tracing::debug!(?ret, ?payload_offset, "convert zc packet to tun packet");
|
tracing::debug!(?ret, ?payload_offset, "convert zc packet to tun packet");
|
||||||
@@ -1362,11 +1361,12 @@ impl NicCtx {
|
|||||||
}
|
}
|
||||||
|
|
||||||
self.global_ctx
|
self.global_ctx
|
||||||
.set_tun_device_ready(nic.ifname().to_string());
|
.issue_event(GlobalCtxEvent::TunDeviceReady(nic.ifname().to_string()));
|
||||||
ret
|
ret
|
||||||
}
|
}
|
||||||
Err(err) => {
|
Err(err) => {
|
||||||
self.global_ctx.set_tun_device_error(err.to_string());
|
self.global_ctx
|
||||||
|
.issue_event(GlobalCtxEvent::TunDeviceError(err.to_string()));
|
||||||
return Err(err);
|
return Err(err);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1405,11 +1405,12 @@ impl NicCtx {
|
|||||||
match nic.create_dev_for_mobile(tun_fd).await {
|
match nic.create_dev_for_mobile(tun_fd).await {
|
||||||
Ok(ret) => {
|
Ok(ret) => {
|
||||||
self.global_ctx
|
self.global_ctx
|
||||||
.set_tun_device_ready(nic.ifname().to_string());
|
.issue_event(GlobalCtxEvent::TunDeviceReady(nic.ifname().to_string()));
|
||||||
ret
|
ret
|
||||||
}
|
}
|
||||||
Err(err) => {
|
Err(err) => {
|
||||||
self.global_ctx.set_tun_device_error(err.to_string());
|
self.global_ctx
|
||||||
|
.issue_event(GlobalCtxEvent::TunDeviceError(err.to_string()));
|
||||||
return Err(err);
|
return Err(err);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -173,12 +173,7 @@ impl EasyTierLauncher {
|
|||||||
#[cfg(mobile)]
|
#[cfg(mobile)]
|
||||||
Self::run_routine_for_mobile(&instance, &data, &mut tasks).await;
|
Self::run_routine_for_mobile(&instance, &data, &mut tasks).await;
|
||||||
|
|
||||||
if let Err(err) = instance.run().await {
|
instance.run().await?;
|
||||||
tasks.abort_all();
|
|
||||||
drop(tasks);
|
|
||||||
instance.clear_resources().await;
|
|
||||||
return Err(err.into());
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(feature = "ffi-dataplane")]
|
#[cfg(feature = "ffi-dataplane")]
|
||||||
data.data_plane
|
data.data_plane
|
||||||
|
|||||||
@@ -477,12 +477,6 @@ struct PublicIpv6Lab {
|
|||||||
extra_bridges: [&'static str; 2],
|
extra_bridges: [&'static str; 2],
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Copy)]
|
|
||||||
enum PublicIpv6LabTopology {
|
|
||||||
DelegatedPrefix,
|
|
||||||
OnLinkPrefix,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl PublicIpv6Lab {
|
impl PublicIpv6Lab {
|
||||||
const PROVIDER_NS: &'static str = "net_a";
|
const PROVIDER_NS: &'static str = "net_a";
|
||||||
const CLIENT_NS: &'static str = "net_b";
|
const CLIENT_NS: &'static str = "net_b";
|
||||||
@@ -496,13 +490,11 @@ impl PublicIpv6Lab {
|
|||||||
const PROVIDER_DEFAULT_FROM: &'static str = "2001:db8:100::/64";
|
const PROVIDER_DEFAULT_FROM: &'static str = "2001:db8:100::/64";
|
||||||
const PROVIDER_WAN_ADDR: &'static str = "2001:db8:ffff:1::2/64";
|
const PROVIDER_WAN_ADDR: &'static str = "2001:db8:ffff:1::2/64";
|
||||||
const UPSTREAM_WAN_ADDR: &'static str = "2001:db8:ffff:1::1/64";
|
const UPSTREAM_WAN_ADDR: &'static str = "2001:db8:ffff:1::1/64";
|
||||||
const ON_LINK_PROVIDER_WAN_ADDR: &'static str = "2001:db8:100::2/64";
|
|
||||||
const ON_LINK_UPSTREAM_WAN_ADDR: &'static str = "2001:db8:100::1/64";
|
|
||||||
const UPSTREAM_SERVER_ADDR: &'static str = "2001:db8:ffff:2::1/64";
|
const UPSTREAM_SERVER_ADDR: &'static str = "2001:db8:ffff:2::1/64";
|
||||||
const SERVER_ADDR: &'static str = "2001:db8:ffff:2::100/64";
|
const SERVER_ADDR: &'static str = "2001:db8:ffff:2::100/64";
|
||||||
const SERVER_IP: &'static str = "2001:db8:ffff:2::100";
|
const SERVER_IP: &'static str = "2001:db8:ffff:2::100";
|
||||||
|
|
||||||
fn setup_with_topology(topology: PublicIpv6LabTopology) -> Self {
|
fn setup() -> Self {
|
||||||
prepare_linux_namespaces();
|
prepare_linux_namespaces();
|
||||||
|
|
||||||
del_netns(Self::UPSTREAM_NS);
|
del_netns(Self::UPSTREAM_NS);
|
||||||
@@ -552,23 +544,13 @@ impl PublicIpv6Lab {
|
|||||||
Self::SERVER_BRIDGE,
|
Self::SERVER_BRIDGE,
|
||||||
);
|
);
|
||||||
|
|
||||||
let (provider_wan_addr, upstream_wan_addr) = match topology {
|
|
||||||
PublicIpv6LabTopology::DelegatedPrefix => {
|
|
||||||
(Self::PROVIDER_WAN_ADDR, Self::UPSTREAM_WAN_ADDR)
|
|
||||||
}
|
|
||||||
PublicIpv6LabTopology::OnLinkPrefix => (
|
|
||||||
Self::ON_LINK_PROVIDER_WAN_ADDR,
|
|
||||||
Self::ON_LINK_UPSTREAM_WAN_ADDR,
|
|
||||||
),
|
|
||||||
};
|
|
||||||
|
|
||||||
run_ip_in_ns(
|
run_ip_in_ns(
|
||||||
Self::PROVIDER_NS,
|
Self::PROVIDER_NS,
|
||||||
&["addr", "add", provider_wan_addr, "dev", "pubwan0"],
|
&["addr", "add", Self::PROVIDER_WAN_ADDR, "dev", "pubwan0"],
|
||||||
);
|
);
|
||||||
run_ip_in_ns(
|
run_ip_in_ns(
|
||||||
Self::UPSTREAM_NS,
|
Self::UPSTREAM_NS,
|
||||||
&["addr", "add", upstream_wan_addr, "dev", "upwan0"],
|
&["addr", "add", Self::UPSTREAM_WAN_ADDR, "dev", "upwan0"],
|
||||||
);
|
);
|
||||||
run_ip_in_ns(
|
run_ip_in_ns(
|
||||||
Self::UPSTREAM_NS,
|
Self::UPSTREAM_NS,
|
||||||
@@ -579,56 +561,37 @@ impl PublicIpv6Lab {
|
|||||||
&["addr", "add", Self::SERVER_ADDR, "dev", "srv0"],
|
&["addr", "add", Self::SERVER_ADDR, "dev", "srv0"],
|
||||||
);
|
);
|
||||||
|
|
||||||
match topology {
|
run_ip_in_ns(
|
||||||
PublicIpv6LabTopology::DelegatedPrefix => {
|
Self::PROVIDER_NS,
|
||||||
run_ip_in_ns(
|
&["link", "add", "pubprefix0", "type", "dummy"],
|
||||||
Self::PROVIDER_NS,
|
);
|
||||||
&["link", "add", "pubprefix0", "type", "dummy"],
|
run_ip_in_ns(Self::PROVIDER_NS, &["link", "set", "pubprefix0", "up"]);
|
||||||
);
|
run_ip_in_ns(
|
||||||
run_ip_in_ns(Self::PROVIDER_NS, &["link", "set", "pubprefix0", "up"]);
|
Self::PROVIDER_NS,
|
||||||
run_ip_in_ns(
|
&[
|
||||||
Self::PROVIDER_NS,
|
"-6",
|
||||||
&[
|
"route",
|
||||||
"-6",
|
"add",
|
||||||
"route",
|
Self::PROVIDER_PREFIX,
|
||||||
"add",
|
"dev",
|
||||||
Self::PROVIDER_PREFIX,
|
"pubprefix0",
|
||||||
"dev",
|
],
|
||||||
"pubprefix0",
|
);
|
||||||
],
|
run_ip_in_ns(
|
||||||
);
|
Self::PROVIDER_NS,
|
||||||
run_ip_in_ns(
|
&[
|
||||||
Self::PROVIDER_NS,
|
"-6",
|
||||||
&[
|
"route",
|
||||||
"-6",
|
"add",
|
||||||
"route",
|
"default",
|
||||||
"add",
|
"from",
|
||||||
"default",
|
Self::PROVIDER_DEFAULT_FROM,
|
||||||
"from",
|
"via",
|
||||||
Self::PROVIDER_DEFAULT_FROM,
|
"2001:db8:ffff:1::1",
|
||||||
"via",
|
"dev",
|
||||||
"2001:db8:ffff:1::1",
|
"pubwan0",
|
||||||
"dev",
|
],
|
||||||
"pubwan0",
|
);
|
||||||
],
|
|
||||||
);
|
|
||||||
}
|
|
||||||
PublicIpv6LabTopology::OnLinkPrefix => {
|
|
||||||
run_ip_in_ns(
|
|
||||||
Self::PROVIDER_NS,
|
|
||||||
&[
|
|
||||||
"-6",
|
|
||||||
"route",
|
|
||||||
"add",
|
|
||||||
"default",
|
|
||||||
"via",
|
|
||||||
"2001:db8:100::1",
|
|
||||||
"dev",
|
|
||||||
"pubwan0",
|
|
||||||
],
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
run_ip_in_ns(
|
run_ip_in_ns(
|
||||||
Self::SERVER_NS,
|
Self::SERVER_NS,
|
||||||
@@ -643,21 +606,19 @@ impl PublicIpv6Lab {
|
|||||||
"srv0",
|
"srv0",
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
if matches!(topology, PublicIpv6LabTopology::DelegatedPrefix) {
|
run_ip_in_ns(
|
||||||
run_ip_in_ns(
|
Self::UPSTREAM_NS,
|
||||||
Self::UPSTREAM_NS,
|
&[
|
||||||
&[
|
"-6",
|
||||||
"-6",
|
"route",
|
||||||
"route",
|
"add",
|
||||||
"add",
|
Self::PROVIDER_PREFIX,
|
||||||
Self::PROVIDER_PREFIX,
|
"via",
|
||||||
"via",
|
"2001:db8:ffff:1::2",
|
||||||
"2001:db8:ffff:1::2",
|
"dev",
|
||||||
"dev",
|
"upwan0",
|
||||||
"upwan0",
|
],
|
||||||
],
|
);
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
run_sysctl_in_ns(Self::PROVIDER_NS, "net.ipv6.conf.all.forwarding=1");
|
run_sysctl_in_ns(Self::PROVIDER_NS, "net.ipv6.conf.all.forwarding=1");
|
||||||
run_sysctl_in_ns(Self::UPSTREAM_NS, "net.ipv6.conf.all.forwarding=1");
|
run_sysctl_in_ns(Self::UPSTREAM_NS, "net.ipv6.conf.all.forwarding=1");
|
||||||
@@ -711,15 +672,7 @@ fn get_public_ipv6_config(
|
|||||||
async fn init_public_ipv6_two_node(
|
async fn init_public_ipv6_two_node(
|
||||||
client_inst_id: uuid::Uuid,
|
client_inst_id: uuid::Uuid,
|
||||||
) -> (PublicIpv6Lab, Instance, Instance) {
|
) -> (PublicIpv6Lab, Instance, Instance) {
|
||||||
init_public_ipv6_two_node_with_topology(client_inst_id, PublicIpv6LabTopology::DelegatedPrefix)
|
let lab = PublicIpv6Lab::setup();
|
||||||
.await
|
|
||||||
}
|
|
||||||
|
|
||||||
async fn init_public_ipv6_two_node_with_topology(
|
|
||||||
client_inst_id: uuid::Uuid,
|
|
||||||
topology: PublicIpv6LabTopology,
|
|
||||||
) -> (PublicIpv6Lab, Instance, Instance) {
|
|
||||||
let lab = PublicIpv6Lab::setup_with_topology(topology);
|
|
||||||
|
|
||||||
let provider_cfg = get_public_ipv6_config(
|
let provider_cfg = get_public_ipv6_config(
|
||||||
"provider_public_ipv6",
|
"provider_public_ipv6",
|
||||||
@@ -803,13 +756,6 @@ fn addr_exists_in_ns(ns: &str, dev: &str, needle: &str) -> bool {
|
|||||||
run_ip_in_ns_output(ns, &["-6", "addr", "show", "dev", dev]).contains(needle)
|
run_ip_in_ns_output(ns, &["-6", "addr", "show", "dev", dev]).contains(needle)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn ndp_proxy_exists_in_ns(ns: &str, dev: &str, addr: std::net::Ipv6Addr) -> bool {
|
|
||||||
let addr = addr.to_string();
|
|
||||||
run_ip_in_ns_output(ns, &["-6", "neigh", "show", "proxy", "dev", dev])
|
|
||||||
.lines()
|
|
||||||
.any(|line| line.split_whitespace().next() == Some(addr.as_str()))
|
|
||||||
}
|
|
||||||
|
|
||||||
#[tokio::test]
|
#[tokio::test]
|
||||||
#[serial_test::serial]
|
#[serial_test::serial]
|
||||||
pub async fn public_ipv6_auto_addr_end_to_end() {
|
pub async fn public_ipv6_auto_addr_end_to_end() {
|
||||||
@@ -932,67 +878,6 @@ pub async fn public_ipv6_auto_addr_end_to_end() {
|
|||||||
drop_insts(vec![provider, client]).await;
|
drop_insts(vec![provider, client]).await;
|
||||||
}
|
}
|
||||||
|
|
||||||
#[tokio::test]
|
|
||||||
#[serial_test::serial]
|
|
||||||
pub async fn public_ipv6_auto_addr_on_link_ndp_proxy_end_to_end() {
|
|
||||||
let client_id = uuid::Uuid::parse_str("44444444-4444-4444-4444-444444444444").unwrap();
|
|
||||||
let (_lab, provider, client) =
|
|
||||||
init_public_ipv6_two_node_with_topology(client_id, PublicIpv6LabTopology::OnLinkPrefix)
|
|
||||||
.await;
|
|
||||||
|
|
||||||
wait_for_condition(
|
|
||||||
|| async {
|
|
||||||
provider
|
|
||||||
.get_global_ctx()
|
|
||||||
.get_advertised_ipv6_public_addr_prefix()
|
|
||||||
== Some(PublicIpv6Lab::PROVIDER_PREFIX.parse().unwrap())
|
|
||||||
},
|
|
||||||
Duration::from_secs(10),
|
|
||||||
)
|
|
||||||
.await;
|
|
||||||
|
|
||||||
let leased = wait_for_public_ipv6_addr(&client).await;
|
|
||||||
wait_for_public_ipv6_route(&provider, leased).await;
|
|
||||||
|
|
||||||
wait_for_condition(
|
|
||||||
|| async {
|
|
||||||
addr_exists_in_ns(
|
|
||||||
PublicIpv6Lab::CLIENT_NS,
|
|
||||||
PublicIpv6Lab::CLIENT_TUN,
|
|
||||||
&leased.to_string(),
|
|
||||||
) && route_exists_in_ns(
|
|
||||||
PublicIpv6Lab::PROVIDER_NS,
|
|
||||||
&format!("{} dev {}", leased.address(), PublicIpv6Lab::PROVIDER_TUN),
|
|
||||||
)
|
|
||||||
},
|
|
||||||
Duration::from_secs(10),
|
|
||||||
)
|
|
||||||
.await;
|
|
||||||
|
|
||||||
wait_for_condition(
|
|
||||||
|| async {
|
|
||||||
ndp_proxy_exists_in_ns(PublicIpv6Lab::PROVIDER_NS, "pubwan0", leased.address())
|
|
||||||
},
|
|
||||||
Duration::from_secs(20),
|
|
||||||
)
|
|
||||||
.await;
|
|
||||||
|
|
||||||
wait_for_condition(
|
|
||||||
|| async {
|
|
||||||
ping6_test(
|
|
||||||
PublicIpv6Lab::SERVER_NS,
|
|
||||||
leased.address().to_string().as_str(),
|
|
||||||
None,
|
|
||||||
)
|
|
||||||
.await
|
|
||||||
},
|
|
||||||
Duration::from_secs(20),
|
|
||||||
)
|
|
||||||
.await;
|
|
||||||
|
|
||||||
drop_insts(vec![provider, client]).await;
|
|
||||||
}
|
|
||||||
|
|
||||||
#[tokio::test]
|
#[tokio::test]
|
||||||
#[serial_test::serial]
|
#[serial_test::serial]
|
||||||
pub async fn public_ipv6_auto_addr_reconnect_reuses_same_address() {
|
pub async fn public_ipv6_auto_addr_reconnect_reuses_same_address() {
|
||||||
|
|||||||
@@ -1,4 +1,3 @@
|
|||||||
use bytes::Buf;
|
|
||||||
use bytes::Bytes;
|
use bytes::Bytes;
|
||||||
use bytes::BytesMut;
|
use bytes::BytesMut;
|
||||||
use zerocopy::AsBytes;
|
use zerocopy::AsBytes;
|
||||||
@@ -487,7 +486,7 @@ impl ZCPacket {
|
|||||||
let total_len = payload_off + payload.len();
|
let total_len = payload_off + payload.len();
|
||||||
ret.inner.reserve(total_len);
|
ret.inner.reserve(total_len);
|
||||||
unsafe { ret.inner.set_len(total_len) };
|
unsafe { ret.inner.set_len(total_len) };
|
||||||
ret.mut_payload().copy_from_slice(payload);
|
ret.mut_payload()[..payload.len()].copy_from_slice(payload);
|
||||||
ret
|
ret
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -588,8 +587,7 @@ impl ZCPacket {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn payload_bytes(mut self) -> BytesMut {
|
pub fn payload_bytes(mut self) -> BytesMut {
|
||||||
self.inner.advance(self.payload_offset());
|
self.inner.split_off(self.payload_offset())
|
||||||
self.inner
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn peer_manager_header(&self) -> Option<&PeerManagerHeader> {
|
pub fn peer_manager_header(&self) -> Option<&PeerManagerHeader> {
|
||||||
@@ -654,12 +652,11 @@ impl ZCPacket {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn tunnel_payload_bytes(mut self) -> BytesMut {
|
pub fn tunnel_payload_bytes(mut self) -> BytesMut {
|
||||||
self.inner.advance(
|
self.inner.split_off(
|
||||||
self.packet_type
|
self.packet_type
|
||||||
.get_packet_offsets()
|
.get_packet_offsets()
|
||||||
.peer_manager_header_offset,
|
.peer_manager_header_offset,
|
||||||
);
|
)
|
||||||
self.inner
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn convert_type(mut self, target_packet_type: ZCPacketType) -> Self {
|
pub fn convert_type(mut self, target_packet_type: ZCPacketType) -> Self {
|
||||||
@@ -705,8 +702,7 @@ impl ZCPacket {
|
|||||||
return Self::new_from_buf(buf, target_packet_type);
|
return Self::new_from_buf(buf, target_packet_type);
|
||||||
}
|
}
|
||||||
|
|
||||||
self.inner.advance(new_offset);
|
Self::new_from_buf(self.inner.split_off(new_offset), target_packet_type)
|
||||||
Self::new_from_buf(self.inner, target_packet_type)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn into_bytes(self) -> Bytes {
|
pub fn into_bytes(self) -> Bytes {
|
||||||
@@ -752,10 +748,8 @@ impl ZCPacket {
|
|||||||
let foreign_hdr_len = hdr.get_header_len();
|
let foreign_hdr_len = hdr.get_header_len();
|
||||||
|
|
||||||
Self::new_from_buf(
|
Self::new_from_buf(
|
||||||
{
|
self.inner
|
||||||
self.inner.advance(foreign_hdr_len + self.payload_offset());
|
.split_off(foreign_hdr_len + self.payload_offset()),
|
||||||
self.inner
|
|
||||||
},
|
|
||||||
ZCPacketType::DummyTunnel,
|
ZCPacketType::DummyTunnel,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user