Compare commits
58 Commits
Author | SHA1 | Date |
---|---|---|
|
43127cf251 | |
|
1614fafe78 | |
|
38535e4219 | |
|
cd4d1ffb0b | |
|
98f1a4a301 | |
|
e29d9053b8 | |
|
5c39692858 | |
|
c40ee944fb | |
|
e9af8b0943 | |
|
4c5833d4e6 | |
|
b9ee44657e | |
|
e2d1f6e2de | |
|
24c1577be9 | |
|
e5b23f0be8 | |
|
9c4cd2ef8c | |
|
083e525c49 | |
|
d22d9d5dec | |
|
5f260d6a86 | |
|
4a7531e02f | |
|
7e93c15eb9 | |
|
7105bee1de | |
|
bc1eeda17f | |
|
8188984938 | |
|
4f36dbf37e | |
|
838e39fe03 | |
|
6a08cd64d9 | |
|
204dc49f57 | |
|
a956db1f91 | |
|
060f55dc2c | |
|
b67d91b083 | |
|
35f1db6bf2 | |
|
1d5ee4e7b3 | |
|
cdf7302422 | |
|
f6604ebf62 | |
|
7be5e4d0af | |
|
acd3949531 | |
|
9d8b95deb4 | |
|
769a2ac528 | |
|
b34f448790 | |
|
b6e12b2f39 | |
|
068abc869b | |
|
03f12695a6 | |
|
40515a58ef | |
|
0a6db8de33 | |
|
99621542d7 | |
|
fca86bbb6a | |
|
3514007090 | |
|
23ba475ceb | |
|
a528bf86e1 | |
|
ecb32b3b46 | |
|
8ba9c25b32 | |
|
c24391b6a0 | |
|
c9649e875a | |
|
a1046af2b8 | |
|
58ce8a49d9 | |
|
6e3d2d04d1 | |
|
8f6f84d098 | |
|
c3828405dc |
|
@ -236,7 +236,7 @@ msrv_build_task:
|
|||
cpu: 2 # Do not increase, will result in scheduling delays
|
||||
memory: "8Gb"
|
||||
# When bumping the image always remember to update the README MSRV as well.
|
||||
image: quay.io/libpod/nv-rust:1.77
|
||||
image: quay.io/libpod/nv-rust:1.83
|
||||
script:
|
||||
- make build
|
||||
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
# This file is automatically @generated by Cargo.
|
||||
# It is not intended for manual editing.
|
||||
version = 3
|
||||
version = 4
|
||||
|
||||
[[package]]
|
||||
name = "addr2line"
|
||||
|
@ -395,9 +395,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "clap"
|
||||
version = "4.5.38"
|
||||
version = "4.5.40"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ed93b9805f8ba930df42c2590f05453d5ec36cbb85d018868a5b24d31f6ac000"
|
||||
checksum = "40b6887a1d8685cebccf115538db5c0efe625ccac9696ad45c409d96566e910f"
|
||||
dependencies = [
|
||||
"clap_builder",
|
||||
"clap_derive",
|
||||
|
@ -405,9 +405,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "clap_builder"
|
||||
version = "4.5.38"
|
||||
version = "4.5.40"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "379026ff283facf611b0ea629334361c4211d1b12ee01024eec1591133b04120"
|
||||
checksum = "e0c66c08ce9f0c698cbce5c0279d0bb6ac936d8674174fe48f736533b964f59e"
|
||||
dependencies = [
|
||||
"anstream",
|
||||
"anstyle",
|
||||
|
@ -417,9 +417,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "clap_derive"
|
||||
version = "4.5.32"
|
||||
version = "4.5.40"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "09176aae279615badda0765c0c0b3f6ed53f4709118af73cf4655d85d1530cd7"
|
||||
checksum = "d2c7947ae4cc3d851207c1adb5b5e260ff0cca11446b1d6d1423788e442257ce"
|
||||
dependencies = [
|
||||
"heck 0.5.0",
|
||||
"proc-macro2",
|
||||
|
@ -487,9 +487,9 @@ checksum = "2a2330da5de22e8a3cb63252ce2abb30116bf5265e89c0e01bc17015ce30a476"
|
|||
|
||||
[[package]]
|
||||
name = "dhcproto"
|
||||
version = "0.9.0"
|
||||
version = "0.12.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "dcee045385d5f7819022821f41209b9945d17550760b0b2349aaef4ecfa14bc3"
|
||||
checksum = "f6794294f2c4665aae452e950c2803a1e487c5672dc8448f0bfa3f52ff67e270"
|
||||
dependencies = [
|
||||
"dhcproto-macros",
|
||||
"hex",
|
||||
|
@ -557,18 +557,6 @@ dependencies = [
|
|||
"syn 1.0.109",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "enum-as-inner"
|
||||
version = "0.6.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a1e6a265c649f3f5979b601d26f1d05ada116434c87741c9493cb56218f76cbc"
|
||||
dependencies = [
|
||||
"heck 0.5.0",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.101",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "enumflags2"
|
||||
version = "0.7.11"
|
||||
|
@ -1003,12 +991,13 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "hyper-util"
|
||||
version = "0.1.11"
|
||||
version = "0.1.15"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "497bbc33a26fdd4af9ed9c70d63f61cf56a938375fbb32df34db9b1cd6d643f2"
|
||||
checksum = "7f66d5bd4c6f02bf0542fad85d626775bab9258cf795a4256dcaf3161114d1df"
|
||||
dependencies = [
|
||||
"bytes",
|
||||
"futures-channel",
|
||||
"futures-core",
|
||||
"futures-util",
|
||||
"http",
|
||||
"http-body",
|
||||
|
@ -1205,6 +1194,17 @@ dependencies = [
|
|||
"hashbrown",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "io-uring"
|
||||
version = "0.7.8"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b86e202f00093dcba4275d4636b93ef9dd75d025ae560d2521b45ea28ab49013"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
"cfg-if",
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ipnet"
|
||||
version = "2.11.0"
|
||||
|
@ -1371,9 +1371,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "mozim"
|
||||
version = "0.2.5"
|
||||
version = "0.2.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "610e34113d007c3588631f879854c14fbf291f3ab7853b833220f7cbf6ece8ad"
|
||||
checksum = "8232b853f83a0c76331d934627aeec172e9d5f2c82d1f9e7f86caa0df72cb304"
|
||||
dependencies = [
|
||||
"byteorder",
|
||||
"dhcproto",
|
||||
|
@ -1382,7 +1382,7 @@ dependencies = [
|
|||
"libc",
|
||||
"log",
|
||||
"nispor",
|
||||
"nix 0.27.1",
|
||||
"nix 0.29.0",
|
||||
"rand 0.8.5",
|
||||
]
|
||||
|
||||
|
@ -1414,7 +1414,7 @@ checksum = "1d87ecb2933e8aeadb3e3a02b828fed80a7528047e68b4f424523a0981a3a084"
|
|||
|
||||
[[package]]
|
||||
name = "netavark"
|
||||
version = "1.15.0"
|
||||
version = "1.16.0-dev"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"chrono",
|
||||
|
@ -1431,18 +1431,16 @@ dependencies = [
|
|||
"log",
|
||||
"mozim",
|
||||
"netlink-packet-core",
|
||||
"netlink-packet-route",
|
||||
"netlink-packet-route 0.23.0",
|
||||
"netlink-sys",
|
||||
"nftables",
|
||||
"nispor",
|
||||
"nix 0.29.0",
|
||||
"nix 0.30.1",
|
||||
"once_cell",
|
||||
"prost",
|
||||
"rand 0.9.1",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"sha2",
|
||||
"sysctl",
|
||||
"tempfile",
|
||||
"tokio",
|
||||
"tokio-stream",
|
||||
|
@ -1490,6 +1488,21 @@ dependencies = [
|
|||
"netlink-packet-utils",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "netlink-packet-route"
|
||||
version = "0.23.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0800eae8638a299eaa67476e1c6b6692922273e0f7939fd188fc861c837b9cd2"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"bitflags",
|
||||
"byteorder",
|
||||
"libc",
|
||||
"log",
|
||||
"netlink-packet-core",
|
||||
"netlink-packet-utils",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "netlink-packet-utils"
|
||||
version = "0.5.2"
|
||||
|
@ -1564,20 +1577,21 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "nix"
|
||||
version = "0.27.1"
|
||||
version = "0.29.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2eb04e9c688eff1c89d72b407f168cf79bb9e867a9d3323ed6c01519eb9cc053"
|
||||
checksum = "71e2746dc3a24dd78b3cfcb7be93368c6de9963d30f43a6a73998a9cf4b17b46"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
"cfg-if",
|
||||
"cfg_aliases",
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "nix"
|
||||
version = "0.29.0"
|
||||
version = "0.30.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "71e2746dc3a24dd78b3cfcb7be93368c6de9963d30f43a6a73998a9cf4b17b46"
|
||||
checksum = "74523f3a35e05aba87a1d978330aef40f67b0304ac79c1c00b294c9830543db6"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
"cfg-if",
|
||||
|
@ -1922,7 +1936,7 @@ dependencies = [
|
|||
"futures",
|
||||
"log",
|
||||
"netlink-packet-core",
|
||||
"netlink-packet-route",
|
||||
"netlink-packet-route 0.22.0",
|
||||
"netlink-packet-utils",
|
||||
"netlink-proto",
|
||||
"netlink-sys",
|
||||
|
@ -1975,15 +1989,6 @@ version = "1.0.20"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "28d3b2b1366ec20994f1fd18c3c594f05c5dd4bc44d8bb0c1c632c8d6829481f"
|
||||
|
||||
[[package]]
|
||||
name = "same-file"
|
||||
version = "1.0.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502"
|
||||
dependencies = [
|
||||
"winapi-util",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "schemars"
|
||||
version = "0.8.22"
|
||||
|
@ -2199,20 +2204,6 @@ dependencies = [
|
|||
"syn 2.0.101",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "sysctl"
|
||||
version = "0.6.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "01198a2debb237c62b6826ec7081082d951f46dbb64b0e8c7649a452230d1dfc"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
"byteorder",
|
||||
"enum-as-inner 0.6.1",
|
||||
"libc",
|
||||
"thiserror 1.0.69",
|
||||
"walkdir",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tempfile"
|
||||
version = "3.20.0"
|
||||
|
@ -2293,16 +2284,18 @@ checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20"
|
|||
|
||||
[[package]]
|
||||
name = "tokio"
|
||||
version = "1.45.0"
|
||||
version = "1.46.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2513ca694ef9ede0fb23fe71a4ee4107cb102b9dc1930f6d0fd77aae068ae165"
|
||||
checksum = "0cc3a2344dafbe23a245241fe8b09735b521110d30fcefbbd5feb1797ca35d17"
|
||||
dependencies = [
|
||||
"backtrace",
|
||||
"bytes",
|
||||
"io-uring",
|
||||
"libc",
|
||||
"mio",
|
||||
"pin-project-lite",
|
||||
"signal-hook-registry",
|
||||
"slab",
|
||||
"socket2",
|
||||
"tokio-macros",
|
||||
"windows-sys 0.52.0",
|
||||
|
@ -2474,7 +2467,7 @@ dependencies = [
|
|||
"async-trait",
|
||||
"cfg-if",
|
||||
"data-encoding",
|
||||
"enum-as-inner 0.5.1",
|
||||
"enum-as-inner",
|
||||
"futures-channel",
|
||||
"futures-io",
|
||||
"futures-util",
|
||||
|
@ -2568,16 +2561,6 @@ version = "0.9.5"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a"
|
||||
|
||||
[[package]]
|
||||
name = "walkdir"
|
||||
version = "2.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "29790946404f91d9c5d06f9874efddea1dc06c5efe94541a7d6863108e3a5e4b"
|
||||
dependencies = [
|
||||
"same-file",
|
||||
"winapi-util",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "want"
|
||||
version = "0.3.1"
|
||||
|
@ -2676,15 +2659,6 @@ version = "0.4.0"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6"
|
||||
|
||||
[[package]]
|
||||
name = "winapi-util"
|
||||
version = "0.1.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "cf221c93e13a30d793f7645a0e7762c55d169dbb0a49671918a2319d289b10bb"
|
||||
dependencies = [
|
||||
"windows-sys 0.59.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "winapi-x86_64-pc-windows-gnu"
|
||||
version = "0.4.0"
|
||||
|
@ -2909,9 +2883,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "zbus"
|
||||
version = "5.6.0"
|
||||
version = "5.9.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2522b82023923eecb0b366da727ec883ace092e7887b61d3da5139f26b44da58"
|
||||
checksum = "4bb4f9a464286d42851d18a605f7193b8febaf5b0919d71c6399b7b26e5b0aad"
|
||||
dependencies = [
|
||||
"async-broadcast",
|
||||
"async-executor",
|
||||
|
@ -2927,7 +2901,7 @@ dependencies = [
|
|||
"futures-core",
|
||||
"futures-lite",
|
||||
"hex",
|
||||
"nix 0.29.0",
|
||||
"nix 0.30.1",
|
||||
"ordered-stream",
|
||||
"serde",
|
||||
"serde_repr",
|
||||
|
@ -2942,9 +2916,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "zbus_macros"
|
||||
version = "5.6.0"
|
||||
version = "5.9.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "05d2e12843c75108c00c618c2e8ef9675b50b6ec095b36dc965f2e5aed463c15"
|
||||
checksum = "ef9859f68ee0c4ee2e8cde84737c78e3f4c54f946f2a38645d0d4c7a95327659"
|
||||
dependencies = [
|
||||
"proc-macro-crate",
|
||||
"proc-macro2",
|
||||
|
@ -3032,9 +3006,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "zvariant"
|
||||
version = "5.5.1"
|
||||
version = "5.5.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "557e89d54880377a507c94cd5452f20e35d14325faf9d2958ebeadce0966c1b2"
|
||||
checksum = "e34d60a2cff1bfddd0d4a73791fd0a4255a59e349aa0cec437cb0df7776f2450"
|
||||
dependencies = [
|
||||
"endi",
|
||||
"enumflags2",
|
||||
|
@ -3046,9 +3020,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "zvariant_derive"
|
||||
version = "5.5.1"
|
||||
version = "5.5.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "757779842a0d242061d24c28be589ce392e45350dfb9186dfd7a042a2e19870c"
|
||||
checksum = "0604376d86f838e0c3f4fe6d6ee1f861a04dac9015cbbb25c25d336996c91423"
|
||||
dependencies = [
|
||||
"proc-macro-crate",
|
||||
"proc-macro2",
|
||||
|
|
20
Cargo.toml
20
Cargo.toml
|
@ -1,6 +1,6 @@
|
|||
[package]
|
||||
name = "netavark"
|
||||
version = "1.15.0"
|
||||
version = "1.16.0-dev"
|
||||
edition = "2021"
|
||||
authors = ["github.com/containers"]
|
||||
license = "Apache-2.0"
|
||||
|
@ -11,7 +11,7 @@ repository = "https://github.com/containers/netavark"
|
|||
categories = ["virtualization"]
|
||||
exclude = ["/.cirrus.yml", "/.github/*", "/hack/*"]
|
||||
build = "build.rs"
|
||||
rust-version = "1.77"
|
||||
rust-version = "1.83"
|
||||
|
||||
[package.metadata.vendor-filter]
|
||||
platforms = ["*-unknown-linux-*"]
|
||||
|
@ -28,7 +28,7 @@ path = "src/dhcp_proxy_client/client.rs"
|
|||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
[dependencies]
|
||||
anyhow = "1.0.93"
|
||||
clap = { version = "~4.5.37", features = ["derive", "env"] }
|
||||
clap = { version = "~4.5.40", features = ["derive", "env"] }
|
||||
env_logger = "0.11.8"
|
||||
ipnet = { version = "2.11.0", features = ["serde"] }
|
||||
iptables = "0.5.2"
|
||||
|
@ -36,27 +36,25 @@ libc = "0.2.157"
|
|||
log = "0.4.27"
|
||||
serde = { version = "1.0.219", features = ["derive"] }
|
||||
serde_json = "1.0.140"
|
||||
sysctl = "0.6.0"
|
||||
zbus = { version = "5.6.0" }
|
||||
nix = { version = "0.29.0", features = ["sched", "signal", "user"] }
|
||||
zbus = { version = "5.9.0" }
|
||||
nix = { version = "0.30.1", features = ["sched", "signal", "user"] }
|
||||
rand = "0.9.1"
|
||||
sha2 = "0.10.9"
|
||||
netlink-packet-route = "0.22.0"
|
||||
netlink-packet-route = "0.23.0"
|
||||
netlink-packet-core = "0.7.0"
|
||||
netlink-sys = "0.8.7"
|
||||
nftables = "0.6.2"
|
||||
fs2 = "0.4.3"
|
||||
tokio = { version = "1.45.0", features = ["rt", "rt-multi-thread", "signal", "fs"] }
|
||||
tokio = { version = "1.46.1", features = ["rt", "rt-multi-thread", "signal", "fs"] }
|
||||
tokio-stream = { version = "0.1.17", features = ["net"] }
|
||||
tonic = "0.13.1"
|
||||
mozim = "0.2.5"
|
||||
mozim = "0.2.6"
|
||||
prost = "0.13.5"
|
||||
futures-channel = "0.3.31"
|
||||
futures-core = "0.3.31"
|
||||
futures-util = "0.3.31"
|
||||
nispor = "1.2.23"
|
||||
tower = { version = "0.5.2", features = ["util"] }
|
||||
hyper-util = "0.1.11"
|
||||
hyper-util = "0.1.15"
|
||||
|
||||
[build-dependencies]
|
||||
chrono = { version = "0.4.41", default-features = false, features = ["clock"] }
|
||||
|
|
|
@ -24,7 +24,7 @@ Netavark is a tool for configuring networking for Linux containers. Its features
|
|||
|
||||
## MSRV (Minimum Supported Rust Version)
|
||||
|
||||
v1.77
|
||||
v1.83
|
||||
|
||||
We test that Netavark can be build on this Rust version and on some newer versions.
|
||||
All newer versions should also build, and if they do not, the issue should be
|
||||
|
|
4
build.rs
4
build.rs
|
@ -72,9 +72,9 @@ fn main() {
|
|||
"nftables" => "nftables",
|
||||
"iptables" => "iptables",
|
||||
"none" => "none",
|
||||
inv => panic!("Invalid default firewall driver {}", inv),
|
||||
inv => panic!("Invalid default firewall driver {inv}"),
|
||||
};
|
||||
println!("cargo:rustc-check-cfg=cfg(default_fw, values(\"nftables\", \"iptables\", \"none\"))");
|
||||
println!("cargo:rustc-cfg=default_fw=\"{}\"", fwdriver);
|
||||
println!("cargo:rustc-cfg=default_fw=\"{fwdriver}\"");
|
||||
println!("cargo:rustc-env=DEFAULT_FW={fwdriver}");
|
||||
}
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
# Source for quay.io/libpod/nv-rust
|
||||
# This version should always match the MSRV, when you update this also update
|
||||
# the version in the root README.md and Cargo.toml.
|
||||
FROM docker.io/library/rust:1.77
|
||||
FROM docker.io/library/rust:1.83
|
||||
RUN apt-get update && apt-get -y install protobuf-compiler libprotobuf-dev
|
||||
|
|
|
@ -82,8 +82,7 @@ Its features include:
|
|||
including MACVLAN networks
|
||||
* All required firewall configuration to perform NAT and port
|
||||
forwarding as required for containers
|
||||
* Support for iptables and firewalld at present, with support
|
||||
for nftables planned in a future release
|
||||
* Support for iptables, firewalld and nftables
|
||||
* Support for rootless containers
|
||||
* Support for IPv4 and IPv6
|
||||
* Support for container DNS resolution via aardvark-dns.
|
||||
|
|
|
@ -56,7 +56,7 @@ struct NetavarkProxyService<W: Write + Clear> {
|
|||
// the timeout for the dora operation
|
||||
dora_timeout: u32,
|
||||
// channel send-side for resetting the inactivity timeout
|
||||
timeout_sender: Arc<Mutex<Sender<i32>>>,
|
||||
timeout_sender: Option<Arc<Mutex<Sender<i32>>>>,
|
||||
// All dhcp poll will be spawned on a new task, keep track of it so
|
||||
// we can remove it on teardown. The key is the container mac.
|
||||
task_map: Arc<Mutex<HashMap<String, AbortHandle>>>,
|
||||
|
@ -64,17 +64,19 @@ struct NetavarkProxyService<W: Write + Clear> {
|
|||
|
||||
impl<W: Write + Clear> NetavarkProxyService<W> {
|
||||
fn reset_inactivity_timeout(&self) {
|
||||
let sender = self.timeout_sender.clone();
|
||||
let locked_sender = match sender.lock() {
|
||||
Ok(v) => v,
|
||||
Err(e) => {
|
||||
log::error!("{}", e);
|
||||
return;
|
||||
if let Some(sender) = &self.timeout_sender {
|
||||
let sender_clone = sender.clone();
|
||||
let locked_sender = match sender_clone.lock() {
|
||||
Ok(v) => v,
|
||||
Err(e) => {
|
||||
log::error!("{e}");
|
||||
return;
|
||||
}
|
||||
};
|
||||
match locked_sender.try_send(1) {
|
||||
Ok(..) => {}
|
||||
Err(e) => log::error!("{e}"),
|
||||
}
|
||||
};
|
||||
match locked_sender.try_send(1) {
|
||||
Ok(..) => {}
|
||||
Err(e) => log::error!("{}", e),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -212,7 +214,7 @@ async fn handle_signal(uds_path: PathBuf) {
|
|||
}
|
||||
}
|
||||
if let Err(e) = fs::remove_file(uds_path) {
|
||||
error!("Could not close uds socket: {}", e);
|
||||
error!("Could not close uds socket: {e}");
|
||||
}
|
||||
|
||||
std::process::exit(0x0100);
|
||||
|
@ -264,7 +266,7 @@ pub async fn serve(opts: Opts) -> NetavarkResult<()> {
|
|||
let fq_cache_path = get_cache_fqname(optional_run_dir);
|
||||
let file = match File::create(&fq_cache_path) {
|
||||
Ok(file) => {
|
||||
debug!("Successfully created leases file: {:?}", fq_cache_path);
|
||||
debug!("Successfully created leases file: {fq_cache_path:?}");
|
||||
file
|
||||
}
|
||||
Err(e) => {
|
||||
|
@ -285,11 +287,18 @@ pub async fn serve(opts: Opts) -> NetavarkResult<()> {
|
|||
|
||||
// Create send and receive channels for activity timeout. If anything is
|
||||
// sent by the tx side, the inactivity timeout is reset
|
||||
let (activity_timeout_tx, activity_timeout_rx) = mpsc::channel(5);
|
||||
let (activity_timeout_tx, activity_timeout_rx) = if inactivity_timeout.as_secs() > 0 {
|
||||
let (tx, rx) = mpsc::channel(5);
|
||||
(Some(tx), Some(rx))
|
||||
} else {
|
||||
(None, None)
|
||||
};
|
||||
let netavark_proxy_service = NetavarkProxyService {
|
||||
cache: cache.clone(),
|
||||
dora_timeout,
|
||||
timeout_sender: Arc::new(Mutex::new(activity_timeout_tx.clone())),
|
||||
timeout_sender: activity_timeout_tx
|
||||
.clone()
|
||||
.map(|tx| Arc::new(Mutex::new(tx))),
|
||||
task_map: Arc::new(Mutex::new(HashMap::new())),
|
||||
};
|
||||
|
||||
|
@ -328,29 +337,31 @@ pub async fn serve(opts: Opts) -> NetavarkResult<()> {
|
|||
///
|
||||
/// ```
|
||||
async fn handle_wakeup<W: Write + Clear>(
|
||||
mut rx: mpsc::Receiver<i32>,
|
||||
rx: Option<mpsc::Receiver<i32>>,
|
||||
timeout_duration: Duration,
|
||||
current_cache: Arc<Mutex<LeaseCache<W>>>,
|
||||
) {
|
||||
loop {
|
||||
match timeout(timeout_duration, rx.recv()).await {
|
||||
Ok(Some(_)) => {
|
||||
debug!("timeout timer reset")
|
||||
}
|
||||
Ok(None) => {
|
||||
println!("timeout channel closed");
|
||||
break;
|
||||
}
|
||||
Err(_) => {
|
||||
// only 'exit' if the timeout is met AND there are no leases
|
||||
// if we do not exit, the activity_timeout is reset
|
||||
if is_catch_empty(current_cache.clone()) {
|
||||
println!(
|
||||
"timeout met: exiting after {} secs of inactivity",
|
||||
timeout_duration.as_secs()
|
||||
);
|
||||
if let Some(mut rx) = rx {
|
||||
loop {
|
||||
match timeout(timeout_duration, rx.recv()).await {
|
||||
Ok(Some(_)) => {
|
||||
debug!("timeout timer reset")
|
||||
}
|
||||
Ok(None) => {
|
||||
println!("timeout channel closed");
|
||||
break;
|
||||
}
|
||||
Err(_) => {
|
||||
// only 'exit' if the timeout is met AND there are no leases
|
||||
// if we do not exit, the activity_timeout is reset
|
||||
if is_catch_empty(current_cache.clone()) {
|
||||
println!(
|
||||
"timeout met: exiting after {} secs of inactivity",
|
||||
timeout_duration.as_secs()
|
||||
);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -376,7 +387,7 @@ fn is_catch_empty<W: Write + Clear>(current_cache: Arc<Mutex<LeaseCache<W>>>) ->
|
|||
v.is_empty()
|
||||
}
|
||||
Err(e) => {
|
||||
log::error!("{}", e);
|
||||
log::error!("{e}");
|
||||
false
|
||||
}
|
||||
}
|
||||
|
|
|
@ -26,7 +26,7 @@ pub fn listen(config_dir: Option<OsString>) -> NetavarkResult<()> {
|
|||
.as_deref()
|
||||
.unwrap_or(OsStr::new(constants::DEFAULT_CONFIG_DIR)),
|
||||
);
|
||||
log::debug!("looking for firewall configs in {:?}", config_dir);
|
||||
log::debug!("looking for firewall configs in {config_dir:?}");
|
||||
|
||||
let conn = Connection::system()?;
|
||||
let proxy = FirewallDDbusProxyBlocking::builder(&conn)
|
||||
|
@ -43,7 +43,7 @@ pub fn listen(config_dir: Option<OsString>) -> NetavarkResult<()> {
|
|||
|
||||
// This loops forever until the process is killed or there is some dbus error.
|
||||
for _ in proxy.0.receive_signal(SIGNAL_NAME)? {
|
||||
log::debug!("got firewalld {} signal", SIGNAL_NAME);
|
||||
log::debug!("got firewalld {SIGNAL_NAME} signal");
|
||||
reload_rules(config_dir, &conn_option);
|
||||
}
|
||||
|
||||
|
|
|
@ -12,8 +12,7 @@ pub mod version;
|
|||
fn get_config_dir(dir: Option<OsString>, cmd: &str) -> NetavarkResult<OsString> {
|
||||
dir.ok_or_else(|| {
|
||||
NetavarkError::msg(format!(
|
||||
"--config not specified but required for netavark {}",
|
||||
cmd
|
||||
"--config not specified but required for netavark {cmd}"
|
||||
))
|
||||
})
|
||||
}
|
||||
|
|
|
@ -152,7 +152,7 @@ impl Setup {
|
|||
);
|
||||
}
|
||||
}
|
||||
debug!("{:#?}", response);
|
||||
debug!("{response:#?}");
|
||||
let response_json = serde_json::to_string(&response)?;
|
||||
println!("{response_json}");
|
||||
debug!("Setup complete");
|
||||
|
|
|
@ -64,7 +64,7 @@ impl<W: Write + Clear> LeaseCache<W> {
|
|||
/// returns: Result<(), Error>
|
||||
///
|
||||
pub fn add_lease(&mut self, mac_addr: &str, lease: &NetavarkLease) -> Result<(), io::Error> {
|
||||
debug!("add lease: {:?}", mac_addr);
|
||||
debug!("add lease: {mac_addr:?}");
|
||||
// Update cache memory with new lease
|
||||
let cache = &mut self.mem;
|
||||
cache.insert(mac_addr.to_string(), vec![lease.clone()]);
|
||||
|
@ -95,7 +95,7 @@ impl<W: Write + Clear> LeaseCache<W> {
|
|||
///
|
||||
/// * `mac_addr`: Mac address of the container
|
||||
pub fn remove_lease(&mut self, mac_addr: &str) -> Result<Lease, io::Error> {
|
||||
debug!("remove lease: {:?}", mac_addr);
|
||||
debug!("remove lease: {mac_addr:?}");
|
||||
let mem = &mut self.mem;
|
||||
// Check and see if the lease exists, if not create an empty one
|
||||
let lease = match mem.get(mac_addr) {
|
||||
|
@ -150,10 +150,7 @@ impl<W: Write + Clear> LeaseCache<W> {
|
|||
writer.flush()
|
||||
}
|
||||
Err(e) => {
|
||||
error!(
|
||||
"Could not clear the writer. Not updating lease information: {:?}",
|
||||
e
|
||||
);
|
||||
error!("Could not clear the writer. Not updating lease information: {e:?}");
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
|
|
@ -84,7 +84,7 @@ fn test_handle_gws() {
|
|||
// IPV4 implementation
|
||||
impl Address<Ipv4Addr> for MacVLAN {
|
||||
fn new(l: &NetavarkLease, interface: &str) -> Result<MacVLAN, ProxyError> {
|
||||
debug!("new ipv4 macvlan for {}", interface);
|
||||
debug!("new ipv4 macvlan for {interface}");
|
||||
let address = match IpAddr::from_str(&l.yiaddr) {
|
||||
Ok(a) => a,
|
||||
Err(e) => {
|
||||
|
@ -94,7 +94,7 @@ impl Address<Ipv4Addr> for MacVLAN {
|
|||
let gateways = match handle_gws(l.gateways.clone(), &l.subnet_mask) {
|
||||
Ok(g) => g,
|
||||
Err(e) => {
|
||||
return Err(ProxyError::new(format!("bad gateways: {}", e)));
|
||||
return Err(ProxyError::new(format!("bad gateways: {e}")));
|
||||
}
|
||||
};
|
||||
let prefix_length = match get_prefix_length_v4(&l.subnet_mask) {
|
||||
|
@ -135,7 +135,7 @@ impl Address<Ipv4Addr> for MacVLAN {
|
|||
// setup takes the DHCP lease and some additional information and
|
||||
// applies the TCP/IP information to the namespace.
|
||||
pub fn setup(lease: &NetavarkLease, interface: &str, ns_path: &str) -> Result<(), ProxyError> {
|
||||
debug!("setting up {}", interface);
|
||||
debug!("setting up {interface}");
|
||||
let vlan = MacVLAN::new(lease, interface)?;
|
||||
let (_, mut netns) = core_utils::open_netlink_sockets(ns_path)?;
|
||||
vlan.add_ip(&mut netns.netlink)?;
|
||||
|
|
|
@ -237,7 +237,12 @@ impl NetworkConfig {
|
|||
let mut client = NetworkConfig::get_client(p.to_string()).await?;
|
||||
let lease = match client.setup(Request::new(self)).await {
|
||||
Ok(l) => l.into_inner(),
|
||||
Err(s) => return Err(s.into()),
|
||||
Err(e) => {
|
||||
return Err(NetavarkError::msg(format!(
|
||||
"get DHCP lease: {}",
|
||||
e.message()
|
||||
)))
|
||||
}
|
||||
};
|
||||
Ok(lease)
|
||||
}
|
||||
|
@ -261,7 +266,12 @@ impl NetworkConfig {
|
|||
let mut client = NetworkConfig::get_client(p.to_string()).await?;
|
||||
let lease = match client.teardown(Request::new(self)).await {
|
||||
Ok(l) => l.into_inner(),
|
||||
Err(e) => return Err(e.into()),
|
||||
Err(e) => {
|
||||
return Err(NetavarkError::msg(format!(
|
||||
"drop DHCP lease: {}",
|
||||
e.message()
|
||||
)))
|
||||
}
|
||||
};
|
||||
Ok(lease)
|
||||
}
|
||||
|
|
|
@ -119,7 +119,6 @@ mod conf_tests {
|
|||
///
|
||||
/// The following stanzas of code should be attributed to https://github.com/vmx/temp-env
|
||||
///
|
||||
|
||||
/// The previous value is restored when the closure completes or panics, before unwinding the
|
||||
/// panic.
|
||||
///
|
||||
|
|
|
@ -1,11 +1,9 @@
|
|||
use clap::{Parser, Subcommand};
|
||||
use commands::{setup, teardown};
|
||||
use std::process;
|
||||
use tonic::{Code, Status};
|
||||
|
||||
use netavark::dhcp_proxy::lib::g_rpc::{Lease, NetworkConfig};
|
||||
use netavark::dhcp_proxy::lib::g_rpc::NetworkConfig;
|
||||
use netavark::dhcp_proxy::proxy_conf::{DEFAULT_NETWORK_CONFIG, DEFAULT_UDS_PATH};
|
||||
use netavark::error::NetavarkError;
|
||||
|
||||
pub mod commands;
|
||||
|
||||
|
@ -53,10 +51,7 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
|
|||
Ok(r) => r,
|
||||
Err(e) => {
|
||||
eprintln!("Error: {e}");
|
||||
match e {
|
||||
NetavarkError::DHCPProxy(status) => process_failure(status),
|
||||
_ => process::exit(1),
|
||||
}
|
||||
process::exit(1);
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -66,26 +61,3 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
|
|||
println!("{}", pp.unwrap_or_else(|_| "".to_string()));
|
||||
Ok(())
|
||||
}
|
||||
|
||||
//
|
||||
// process_failure makes the client exit with a specific
|
||||
// error code
|
||||
//
|
||||
fn process_failure(status: Status) -> Lease {
|
||||
let mut rc: i32 = 1;
|
||||
|
||||
match status.code() {
|
||||
Code::Unknown => {
|
||||
rc = 155;
|
||||
}
|
||||
Code::InvalidArgument => {
|
||||
rc = 156;
|
||||
}
|
||||
Code::DeadlineExceeded => {}
|
||||
Code::NotFound => {
|
||||
rc = 6;
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
process::exit(rc)
|
||||
}
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
use crate::error::{NetavarkError, NetavarkResult};
|
||||
use crate::network::core_utils::is_using_systemd;
|
||||
|
||||
use fs2::FileExt;
|
||||
use libc::pid_t;
|
||||
|
@ -16,7 +17,6 @@ use std::net::{IpAddr, Ipv6Addr};
|
|||
use std::path::{Path, PathBuf};
|
||||
use std::process::{Command, Stdio};
|
||||
|
||||
const SYSTEMD_CHECK_PATH: &str = "/run/systemd/system";
|
||||
const SYSTEMD_RUN: &str = "systemd-run";
|
||||
const AARDVARK_COMMIT_LOCK: &str = "aardvark.lock";
|
||||
|
||||
|
@ -108,12 +108,12 @@ impl Aardvark {
|
|||
false
|
||||
}
|
||||
|
||||
pub fn start_aardvark_server(&self) -> Result<()> {
|
||||
pub fn start_aardvark_server(&self) -> NetavarkResult<()> {
|
||||
log::debug!("Spawning aardvark server");
|
||||
|
||||
let mut aardvark_args = vec![];
|
||||
// only use systemd when it is booted, see sd_booted(3)
|
||||
if Path::new(SYSTEMD_CHECK_PATH).exists() && Aardvark::is_executable_in_path(SYSTEMD_RUN) {
|
||||
// only use systemd when it is booted
|
||||
if is_using_systemd() && Aardvark::is_executable_in_path(SYSTEMD_RUN) {
|
||||
// TODO: This could be replaced by systemd-api.
|
||||
aardvark_args = vec![
|
||||
OsStr::new(SYSTEMD_RUN),
|
||||
|
@ -135,7 +135,7 @@ impl Aardvark {
|
|||
OsStr::new("run"),
|
||||
]);
|
||||
|
||||
log::debug!("start aardvark-dns: {:?}", aardvark_args);
|
||||
log::debug!("start aardvark-dns: {aardvark_args:?}");
|
||||
|
||||
// After https://github.com/containers/aardvark-dns/pull/148 this command
|
||||
// will block till aardvark-dns's parent process returns back and let
|
||||
|
@ -152,23 +152,19 @@ impl Aardvark {
|
|||
return Ok(());
|
||||
}
|
||||
if out.stderr.is_empty() {
|
||||
return Err(std::io::Error::new(
|
||||
std::io::ErrorKind::Other,
|
||||
return Err(NetavarkError::msg(
|
||||
"aardvark-dns exited unexpectedly without error message",
|
||||
));
|
||||
}
|
||||
// aardvark-dns failed capture stderr
|
||||
let msg = String::from_utf8(out.stderr).map_err(|e| {
|
||||
std::io::Error::new(
|
||||
std::io::ErrorKind::Other,
|
||||
format!("failed to parse aardvark-dns stderr message: {e}"),
|
||||
)
|
||||
NetavarkError::msg(format!("failed to parse aardvark-dns stderr message: {e}"))
|
||||
})?;
|
||||
|
||||
Err(std::io::Error::new(
|
||||
std::io::ErrorKind::Other,
|
||||
format!("aardvark-dns failed to start: {}", msg.trim()),
|
||||
))
|
||||
Err(NetavarkError::msg(format!(
|
||||
"aardvark-dns failed to start: {}",
|
||||
msg.trim()
|
||||
)))
|
||||
}
|
||||
|
||||
fn check_netns(&self, pid: pid_t) {
|
||||
|
@ -235,7 +231,7 @@ impl Aardvark {
|
|||
Ok(())
|
||||
}
|
||||
|
||||
pub fn commit_entries(&self, entries: &[AardvarkEntry]) -> Result<()> {
|
||||
pub fn commit_entries(&self, entries: &[AardvarkEntry]) -> NetavarkResult<()> {
|
||||
// Acquire fs lock to ensure other instance of aardvark cannot commit
|
||||
// or start aardvark instance till already running instance has not
|
||||
// completed its `commit` phase.
|
||||
|
@ -251,17 +247,16 @@ impl Aardvark {
|
|||
{
|
||||
Ok(file) => file,
|
||||
Err(e) => {
|
||||
return Err(std::io::Error::new(
|
||||
std::io::ErrorKind::Other,
|
||||
format!("Failed to open/create lockfile {:?}: {}", &lockfile_path, e),
|
||||
));
|
||||
return Err(NetavarkError::msg(format!(
|
||||
"Failed to open/create lockfile {:?}: {}",
|
||||
&lockfile_path, e
|
||||
)));
|
||||
}
|
||||
};
|
||||
if let Err(er) = lockfile.lock_exclusive() {
|
||||
return Err(std::io::Error::new(
|
||||
std::io::ErrorKind::Other,
|
||||
format!("Failed to acquire exclusive lock on {lockfile_path:?}: {er}"),
|
||||
));
|
||||
return Err(NetavarkError::msg(format!(
|
||||
"Failed to acquire exclusive lock on {lockfile_path:?}: {er}"
|
||||
)));
|
||||
}
|
||||
|
||||
for entry in entries {
|
||||
|
@ -307,15 +302,14 @@ impl Aardvark {
|
|||
OpenOptions::new().append(true).open(&path)?
|
||||
}
|
||||
Err(e) => {
|
||||
return Err(e);
|
||||
return Err(NetavarkError::Io(e));
|
||||
}
|
||||
};
|
||||
match Aardvark::commit_entry(entry, file) {
|
||||
Err(er) => {
|
||||
return Err(std::io::Error::new(
|
||||
std::io::ErrorKind::Other,
|
||||
format!("Failed to commit entry {entry:?}: {er}"),
|
||||
));
|
||||
return Err(NetavarkError::msg(format!(
|
||||
"Failed to commit entry {entry:?}: {er}"
|
||||
)));
|
||||
}
|
||||
Ok(_) => continue,
|
||||
}
|
||||
|
|
|
@ -72,14 +72,10 @@ pub enum NetavarkError {
|
|||
Dbus(zbus::Error),
|
||||
DbusVariant(zbus::zvariant::Error),
|
||||
|
||||
Sysctl(sysctl::SysctlError),
|
||||
|
||||
Serde(serde_json::Error),
|
||||
|
||||
Netlink(netlink_packet_core::error::ErrorMessage),
|
||||
|
||||
DHCPProxy(tonic::Status),
|
||||
|
||||
List(NetavarkErrorList),
|
||||
|
||||
Nftables(nftables::helper::NftablesError),
|
||||
|
@ -150,10 +146,8 @@ impl fmt::Display for NetavarkError {
|
|||
NetavarkError::Io(e) => write!(f, "IO error: {e}"),
|
||||
NetavarkError::Dbus(e) => write!(f, "DBus error: {e}"),
|
||||
NetavarkError::DbusVariant(e) => write!(f, "DBus Variant Error: {e}"),
|
||||
NetavarkError::Sysctl(e) => write!(f, "Sysctl error: {e}"),
|
||||
NetavarkError::Serde(e) => write!(f, "JSON Decoding error: {e}"),
|
||||
NetavarkError::Netlink(e) => write!(f, "Netlink error: {e}"),
|
||||
NetavarkError::DHCPProxy(e) => write!(f, "dhcp proxy error: {e}"),
|
||||
NetavarkError::List(list) => {
|
||||
if list.0.len() == 1 {
|
||||
write!(f, "{}", list.0[0])
|
||||
|
@ -200,12 +194,6 @@ impl From<zbus::zvariant::signature::Error> for NetavarkError {
|
|||
}
|
||||
}
|
||||
|
||||
impl From<sysctl::SysctlError> for NetavarkError {
|
||||
fn from(err: sysctl::SysctlError) -> NetavarkError {
|
||||
NetavarkError::Sysctl(err)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<serde_json::Error> for NetavarkError {
|
||||
fn from(err: serde_json::Error) -> NetavarkError {
|
||||
NetavarkError::Serde(err)
|
||||
|
@ -224,12 +212,6 @@ impl From<netlink_packet_core::error::ErrorMessage> for NetavarkError {
|
|||
}
|
||||
}
|
||||
|
||||
impl From<tonic::Status> for NetavarkError {
|
||||
fn from(err: tonic::Status) -> Self {
|
||||
NetavarkError::DHCPProxy(err)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<nftables::helper::NftablesError> for NetavarkError {
|
||||
fn from(err: nftables::helper::NftablesError) -> Self {
|
||||
NetavarkError::Nftables(err)
|
||||
|
|
|
@ -147,7 +147,7 @@ impl firewall::FirewallDriver for FirewallD {
|
|||
|
||||
if let Some(subnets) = tear.config.subnets {
|
||||
for subnet in subnets {
|
||||
debug!("Removing subnet {} from zone {}", subnet, ZONENAME);
|
||||
debug!("Removing subnet {subnet} from zone {ZONENAME}");
|
||||
let _ = self.conn.call_method(
|
||||
Some("org.fedoraproject.FirewallD1"),
|
||||
"/org/fedoraproject/FirewallD1",
|
||||
|
@ -208,7 +208,7 @@ impl firewall::FirewallDriver for FirewallD {
|
|||
if port.host_ip == "127.0.0.1" {
|
||||
if let Some(v4) = setup_portfw.container_ip_v4 {
|
||||
let rule = get_localhost_pf_rich_rule(port, &v4);
|
||||
debug!("Adding localhost pf rule: {}", rule);
|
||||
debug!("Adding localhost pf rule: {rule}");
|
||||
localhost_rich_rules.append(Value::new(rule))?;
|
||||
}
|
||||
continue;
|
||||
|
@ -247,7 +247,7 @@ impl firewall::FirewallDriver for FirewallD {
|
|||
port_forwarding_rules
|
||||
.append(Value::new(make_port_tuple(port, &v4.to_string())))?;
|
||||
let localhost_rule = get_localhost_pf_rich_rule(port, &v4);
|
||||
debug!("Adding localhost pf rule: {}", localhost_rule);
|
||||
debug!("Adding localhost pf rule: {localhost_rule}");
|
||||
localhost_rich_rules.append(Value::new(localhost_rule))?;
|
||||
}
|
||||
}
|
||||
|
@ -430,13 +430,13 @@ impl firewall::FirewallDriver for FirewallD {
|
|||
};
|
||||
let mut is_match = false;
|
||||
if let Some(v4) = &ipv4 {
|
||||
debug!("Checking firewalld IP {} against our IP {}", port_ip, v4);
|
||||
debug!("Checking firewalld IP {port_ip} against our IP {v4}");
|
||||
if *v4 == port_ip {
|
||||
is_match = true;
|
||||
}
|
||||
}
|
||||
if let Some(v6) = &ipv6 {
|
||||
debug!("Checking firewalld IP {} against our IP {}", port_ip, v6);
|
||||
debug!("Checking firewalld IP {port_ip} against our IP {v6}");
|
||||
if *v6 == port_ip {
|
||||
is_match = true;
|
||||
}
|
||||
|
@ -508,14 +508,14 @@ impl firewall::FirewallDriver for FirewallD {
|
|||
|
||||
// Remove any rule using our IPv4 or IPv6 as daddr.
|
||||
if let Some(v4) = &ipv4 {
|
||||
let daddr = format!("to-addr=\"{}\"", v4);
|
||||
debug!("Checking if {} contains string {}", old_rule, daddr);
|
||||
let daddr = format!("to-addr=\"{v4}\"");
|
||||
debug!("Checking if {old_rule} contains string {daddr}");
|
||||
if old_rule.to_string().contains(&daddr) {
|
||||
is_match = true;
|
||||
}
|
||||
}
|
||||
if let Some(v6) = &ipv6 {
|
||||
let daddr = format!("to-addr=\"{}\"", v6);
|
||||
let daddr = format!("to-addr=\"{v6}\"");
|
||||
if old_rule.to_string().contains(&daddr) {
|
||||
is_match = true;
|
||||
}
|
||||
|
@ -572,8 +572,8 @@ impl firewall::FirewallDriver for FirewallD {
|
|||
// Remove any rule using our IPv4 as daddr.
|
||||
// We don't do IPv6 localhost forwarding.
|
||||
if let Some(v4) = &ipv4 {
|
||||
let daddr = format!("to-addr=\"{}\"", v4);
|
||||
debug!("Checking if {} contains string {}", old_rule, daddr);
|
||||
let daddr = format!("to-addr=\"{v4}\"");
|
||||
debug!("Checking if {old_rule} contains string {daddr}");
|
||||
if old_rule.to_string().contains(&daddr) {
|
||||
is_match = true;
|
||||
}
|
||||
|
@ -623,7 +623,7 @@ impl firewall::FirewallDriver for FirewallD {
|
|||
|
||||
/// Create a firewalld zone to hold all our interfaces.
|
||||
fn create_zone_if_not_exist(conn: &Connection, zone_name: &str) -> NetavarkResult<bool> {
|
||||
debug!("Creating firewall zone {}", zone_name);
|
||||
debug!("Creating firewall zone {zone_name}");
|
||||
|
||||
// First, double-check if the zone exists in the running config.
|
||||
let zones_msg = conn.call_method(
|
||||
|
@ -701,11 +701,11 @@ pub fn add_source_subnets_to_zone(
|
|||
"Error decoding DBus message for zone of subnet"
|
||||
)?;
|
||||
if zone_string == zone_name {
|
||||
debug!("Subnet {} already exists in zone {}", net, zone_name);
|
||||
debug!("Subnet {net} already exists in zone {zone_name}");
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
debug!("Adding subnet {} to zone {} as source", net, zone_name);
|
||||
debug!("Adding subnet {net} to zone {zone_name} as source");
|
||||
|
||||
let _ = conn.call_method(
|
||||
Some("org.fedoraproject.FirewallD1"),
|
||||
|
@ -730,8 +730,7 @@ fn add_policy_if_not_exist(
|
|||
priority: Option<i16>,
|
||||
) -> NetavarkResult<bool> {
|
||||
debug!(
|
||||
"Adding firewalld policy {} (ingress zone {}, egress zone {})",
|
||||
policy_name, ingress_zone_name, egress_zone_name
|
||||
"Adding firewalld policy {policy_name} (ingress zone {ingress_zone_name}, egress zone {egress_zone_name})"
|
||||
);
|
||||
|
||||
// Does policy exist in running policies?
|
||||
|
@ -829,7 +828,7 @@ fn make_port_tuple(port: &PortMapping, addr: &str) -> (String, String, String, S
|
|||
port.container_port.to_string(),
|
||||
addr.to_string(),
|
||||
);
|
||||
debug!("Port is {:?}", to_return);
|
||||
debug!("Port is {to_return:?}");
|
||||
to_return
|
||||
}
|
||||
}
|
||||
|
@ -882,10 +881,7 @@ fn update_policy_config(
|
|||
Ok(_) => {}
|
||||
Err(e) => {
|
||||
return Err(NetavarkError::wrap(
|
||||
format!(
|
||||
"Failed to update firewalld policy {} port forwarding rules",
|
||||
policy_name
|
||||
),
|
||||
format!("Failed to update firewalld policy {policy_name} port forwarding rules"),
|
||||
e.into(),
|
||||
))
|
||||
}
|
||||
|
@ -964,7 +960,7 @@ fn get_pf_rich_rule(
|
|||
ctr_port: &str,
|
||||
ctr_ip: &str,
|
||||
) -> String {
|
||||
format!("rule family=\"{}\" destination address=\"{}\" forward-port port=\"{}\" protocol=\"{}\" to-port=\"{}\" to-addr=\"{}\"", ip_family, host_ip, host_port, protocol, ctr_port, ctr_ip)
|
||||
format!("rule family=\"{ip_family}\" destination address=\"{host_ip}\" forward-port port=\"{host_port}\" protocol=\"{protocol}\" to-port=\"{ctr_port}\" to-addr=\"{ctr_ip}\"")
|
||||
}
|
||||
|
||||
/// Check if firewalld is running.
|
||||
|
@ -993,14 +989,11 @@ pub fn add_firewalld_if_possible(dbus_conn: &Option<Connection>, net: &ipnet::Ip
|
|||
if !is_firewalld_running(conn) {
|
||||
return;
|
||||
}
|
||||
debug!("Adding firewalld rules for network {}", net);
|
||||
debug!("Adding firewalld rules for network {net}");
|
||||
|
||||
match add_source_subnets_to_zone(conn, "trusted", &[*net]) {
|
||||
Ok(_) => {}
|
||||
Err(e) => warn!(
|
||||
"Error adding subnet {} from firewalld trusted zone: {}",
|
||||
net, e
|
||||
),
|
||||
Err(e) => warn!("Error adding subnet {net} from firewalld trusted zone: {e}"),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1016,7 +1009,7 @@ pub fn rm_firewalld_if_possible(net: &ipnet::IpNet) {
|
|||
if !is_firewalld_running(&conn) {
|
||||
return;
|
||||
}
|
||||
debug!("Removing firewalld rules for IPs {}", net);
|
||||
debug!("Removing firewalld rules for IPs {net}");
|
||||
match conn.call_method(
|
||||
Some("org.fedoraproject.FirewallD1"),
|
||||
"/org/fedoraproject/FirewallD1",
|
||||
|
@ -1025,10 +1018,7 @@ pub fn rm_firewalld_if_possible(net: &ipnet::IpNet) {
|
|||
&("trusted", net.to_string()),
|
||||
) {
|
||||
Ok(_) => {}
|
||||
Err(e) => warn!(
|
||||
"Error removing subnet {} from firewalld trusted zone: {}",
|
||||
net, e
|
||||
),
|
||||
Err(e) => warn!("Error removing subnet {net} from firewalld trusted zone: {e}"),
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -1057,15 +1047,12 @@ pub fn is_firewalld_strict_forward_enabled(dbus_con: &Option<Connection>) -> boo
|
|||
Ok(v) => match v.downcast::<String>() {
|
||||
Ok(s) => s,
|
||||
Err(e) => {
|
||||
warn!(
|
||||
"couldn't downcast StrictForwardPorts value to string: {}",
|
||||
e
|
||||
);
|
||||
warn!("couldn't downcast StrictForwardPorts value to string: {e}");
|
||||
return false;
|
||||
}
|
||||
},
|
||||
Err(e) => {
|
||||
warn!("couldn't retrieve StrictForwardPorts property: {}", e);
|
||||
warn!("couldn't retrieve StrictForwardPorts property: {e}");
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
@ -1073,10 +1060,7 @@ pub fn is_firewalld_strict_forward_enabled(dbus_con: &Option<Connection>) -> boo
|
|||
"yes" => true,
|
||||
"no" => false,
|
||||
other => {
|
||||
warn!(
|
||||
"unexpected value from StrictForwardPorts property: {}",
|
||||
other
|
||||
);
|
||||
warn!("unexpected value from StrictForwardPorts property: {other}");
|
||||
false
|
||||
}
|
||||
}
|
||||
|
|
|
@ -126,11 +126,9 @@ impl firewall::FirewallDriver for IptablesDriver {
|
|||
let subnet_v4 = match setup_portfw.subnet_v4 {
|
||||
Some(s) => s,
|
||||
None => {
|
||||
return Err(std::io::Error::new(
|
||||
std::io::ErrorKind::Other,
|
||||
return Err(NetavarkError::msg(
|
||||
"ipv4 address but provided but no v4 subnet provided",
|
||||
)
|
||||
.into())
|
||||
))
|
||||
}
|
||||
};
|
||||
let chains =
|
||||
|
@ -141,11 +139,9 @@ impl firewall::FirewallDriver for IptablesDriver {
|
|||
let subnet_v6 = match setup_portfw.subnet_v6 {
|
||||
Some(s) => s,
|
||||
None => {
|
||||
return Err(std::io::Error::new(
|
||||
std::io::ErrorKind::Other,
|
||||
return Err(NetavarkError::msg(
|
||||
"ipv6 address but provided but no v6 subnet provided",
|
||||
)
|
||||
.into())
|
||||
))
|
||||
}
|
||||
};
|
||||
let chains =
|
||||
|
@ -160,11 +156,9 @@ impl firewall::FirewallDriver for IptablesDriver {
|
|||
let subnet_v4 = match tear.config.subnet_v4 {
|
||||
Some(s) => s,
|
||||
None => {
|
||||
return Err(std::io::Error::new(
|
||||
std::io::ErrorKind::Other,
|
||||
return Err(NetavarkError::msg(
|
||||
"ipv4 address but provided but no v4 subnet provided",
|
||||
)
|
||||
.into())
|
||||
))
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -193,11 +187,9 @@ impl firewall::FirewallDriver for IptablesDriver {
|
|||
let subnet_v6 = match tear.config.subnet_v6 {
|
||||
Some(s) => s,
|
||||
None => {
|
||||
return Err(std::io::Error::new(
|
||||
std::io::ErrorKind::Other,
|
||||
return Err(NetavarkError::msg(
|
||||
"ipv6 address but provided but no v6 subnet provided",
|
||||
)
|
||||
.into())
|
||||
))
|
||||
}
|
||||
};
|
||||
|
||||
|
|
|
@ -807,9 +807,9 @@ fn get_subnet_chain_name(subnet: IpNet, net_id: &str, dnat: bool) -> Cow<str> {
|
|||
};
|
||||
|
||||
if dnat {
|
||||
Cow::Owned(format!("nv_{}_{}_dnat", net_id_clean, subnet_clean))
|
||||
Cow::Owned(format!("nv_{net_id_clean}_{subnet_clean}_dnat"))
|
||||
} else {
|
||||
Cow::Owned(format!("nv_{}_{}", net_id_clean, subnet_clean))
|
||||
Cow::Owned(format!("nv_{net_id_clean}_{subnet_clean}"))
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1243,7 +1243,7 @@ fn get_matching_rules_in_chain<'a, F: Fn(&schema::Rule) -> bool>(
|
|||
}
|
||||
|
||||
if rule_match(r) {
|
||||
log::debug!("Matched {:?}", r);
|
||||
log::debug!("Matched {r:?}");
|
||||
rules.push(r.clone());
|
||||
}
|
||||
}
|
||||
|
@ -1263,7 +1263,7 @@ fn get_chain<'a>(base_rules: &schema::Nftables<'a>, chain: &str) -> Option<schem
|
|||
schema::NfObject::ListObject(obj) => match obj {
|
||||
schema::NfListObject::Chain(c) => {
|
||||
if c.name == *chain {
|
||||
log::debug!("Found chain {}", chain);
|
||||
log::debug!("Found chain {chain}");
|
||||
return Some(c.clone());
|
||||
}
|
||||
}
|
||||
|
|
|
@ -87,30 +87,21 @@ pub fn remove_if_rule_exists(
|
|||
}
|
||||
|
||||
fn debug_chain_create(table: &str, chain: &str) {
|
||||
debug!("chain {} created on table {}", chain, table);
|
||||
debug!("chain {chain} created on table {table}");
|
||||
}
|
||||
|
||||
fn debug_chain_exists(table: &str, chain: &str) {
|
||||
debug!("chain {} exists on table {}", chain, table);
|
||||
debug!("chain {chain} exists on table {table}");
|
||||
}
|
||||
|
||||
pub fn debug_rule_create(table: &str, chain: &str, rule: String) {
|
||||
debug!(
|
||||
"rule {} created on table {} and chain {}",
|
||||
rule, table, chain
|
||||
);
|
||||
debug!("rule {rule} created on table {table} and chain {chain}");
|
||||
}
|
||||
|
||||
fn debug_rule_exists(table: &str, chain: &str, rule: String) {
|
||||
debug!(
|
||||
"rule {} exists on table {} and chain {}",
|
||||
rule, table, chain
|
||||
);
|
||||
debug!("rule {rule} exists on table {table} and chain {chain}");
|
||||
}
|
||||
|
||||
fn debug_rule_no_exists(table: &str, chain: &str, rule: String) {
|
||||
debug!(
|
||||
"no rule {} exists on table {} and chain {}",
|
||||
rule, table, chain
|
||||
);
|
||||
debug!("no rule {rule} exists on table {table} and chain {chain}");
|
||||
}
|
||||
|
|
|
@ -347,7 +347,7 @@ pub fn get_network_chains<'a>(
|
|||
chains.push(netavark_isolation_chain_3);
|
||||
|
||||
forward_chain.build_rule(VarkRule {
|
||||
rule: format!("{} {}", NETAVARK_FIREWALL_RULE_BUILDER, NETAVARK_FORWARD),
|
||||
rule: format!("{NETAVARK_FIREWALL_RULE_BUILDER} {NETAVARK_FORWARD}"),
|
||||
position: Some(ind),
|
||||
td_policy: Some(TeardownPolicy::Never),
|
||||
});
|
||||
|
@ -360,7 +360,7 @@ pub fn get_network_chains<'a>(
|
|||
|
||||
// Add NETAVARK_INPUT chain to INPUT chain
|
||||
input_chain.build_rule(VarkRule {
|
||||
rule: format!("{} {}", NETAVARK_FIREWALL_RULE_BUILDER, NETAVARK_INPUT),
|
||||
rule: format!("{NETAVARK_FIREWALL_RULE_BUILDER} {NETAVARK_INPUT}"),
|
||||
position: Some(1),
|
||||
td_policy: Some(TeardownPolicy::Never),
|
||||
});
|
||||
|
@ -375,10 +375,7 @@ pub fn get_network_chains<'a>(
|
|||
// to gateway when using bridge network with internal dns.
|
||||
for proto in ["udp", "tcp"] {
|
||||
netavark_input_chain.build_rule(VarkRule::new(
|
||||
format!(
|
||||
"-p {} -s {} --dport {} -j {}",
|
||||
proto, network, dns_port, ACCEPT
|
||||
),
|
||||
format!("-p {proto} -s {network} --dport {dns_port} -j {ACCEPT}"),
|
||||
Some(TeardownPolicy::OnComplete),
|
||||
));
|
||||
}
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
use std::{collections::HashMap, net::IpAddr, os::fd::BorrowedFd, sync::Once};
|
||||
use std::{collections::HashMap, fs, net::IpAddr, os::fd::BorrowedFd};
|
||||
|
||||
use ipnet::IpNet;
|
||||
use log::{debug, error};
|
||||
|
@ -8,6 +8,7 @@ use netlink_packet_route::link::{
|
|||
};
|
||||
|
||||
use crate::dns::aardvark::SafeString;
|
||||
use crate::network::core_utils::get_default_route_interface;
|
||||
use crate::network::dhcp::{dhcp_teardown, get_dhcp_lease};
|
||||
use crate::{
|
||||
dns::aardvark::AardvarkEntry,
|
||||
|
@ -17,7 +18,7 @@ use crate::{
|
|||
iptables::MAX_HASH_SIZE,
|
||||
state::{remove_fw_config, write_fw_config},
|
||||
},
|
||||
network::{core_utils::disable_ipv6_autoconf, types},
|
||||
network::{constants, sysctl::disable_ipv6_autoconf, types},
|
||||
};
|
||||
|
||||
use super::{
|
||||
|
@ -26,13 +27,13 @@ use super::{
|
|||
NO_CONTAINER_INTERFACE_ERROR, OPTION_HOST_INTERFACE_NAME, OPTION_ISOLATE, OPTION_METRIC,
|
||||
OPTION_MODE, OPTION_MTU, OPTION_NO_DEFAULT_ROUTE, OPTION_VLAN, OPTION_VRF,
|
||||
},
|
||||
core_utils::{self, get_ipam_addresses, join_netns, parse_option, CoreUtils},
|
||||
core_utils::{self, get_ipam_addresses, is_using_systemd, join_netns, parse_option, CoreUtils},
|
||||
driver::{self, DriverInfo},
|
||||
internal_types::{
|
||||
IPAMAddresses, IsolateOption, PortForwardConfig, SetupNetwork, TearDownNetwork,
|
||||
TeardownPortForward,
|
||||
},
|
||||
netlink,
|
||||
netlink, sysctl,
|
||||
types::StatusBlock,
|
||||
};
|
||||
|
||||
|
@ -163,22 +164,14 @@ impl driver::NetworkDriver for Bridge<'_> {
|
|||
data.bridge_interface_name, data.ipam.gateway_addresses
|
||||
);
|
||||
|
||||
if let BridgeMode::Managed = data.mode {
|
||||
if !self.info.network.internal {
|
||||
setup_ipv4_fw_sysctl()?;
|
||||
if data.ipam.ipv6_enabled {
|
||||
setup_ipv6_fw_sysctl()?;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let (host_sock, netns_sock) = netlink_sockets;
|
||||
|
||||
let container_veth_mac = create_interfaces(
|
||||
let (container_veth_mac, sysctl_writer) = create_interfaces(
|
||||
host_sock,
|
||||
netns_sock,
|
||||
data,
|
||||
self.info.network.internal,
|
||||
self.info.rootless,
|
||||
self.info.netns_host,
|
||||
self.info.netns_container,
|
||||
)?;
|
||||
|
@ -227,6 +220,11 @@ impl driver::NetworkDriver for Bridge<'_> {
|
|||
let _ = response
|
||||
.dns_server_ips
|
||||
.insert(data.ipam.nameservers.clone());
|
||||
// Note: this is being added so podman setup is backward compatible with the design
|
||||
// which we had with dnsname/dnsmasq.
|
||||
let _ = response
|
||||
.dns_search_domains
|
||||
.insert(vec![constants::PODMAN_DEFAULT_SEARCH_DOMAIN.to_string()]);
|
||||
|
||||
let mut ipv4 = Vec::new();
|
||||
let mut ipv6 = Vec::new();
|
||||
|
@ -290,28 +288,14 @@ impl driver::NetworkDriver for Bridge<'_> {
|
|||
};
|
||||
|
||||
if let BridgeMode::Managed = data.mode {
|
||||
// if the network is internal block routing and do not setup firewall rules
|
||||
if self.info.network.internal {
|
||||
CoreUtils::apply_sysctl_value(
|
||||
format!(
|
||||
"/proc/sys/net/ipv4/conf/{}/forwarding",
|
||||
data.bridge_interface_name
|
||||
),
|
||||
"0",
|
||||
)?;
|
||||
if data.ipam.ipv6_enabled {
|
||||
CoreUtils::apply_sysctl_value(
|
||||
format!(
|
||||
"/proc/sys/net/ipv6/conf/{}/forwarding",
|
||||
data.bridge_interface_name
|
||||
),
|
||||
"0",
|
||||
)?;
|
||||
}
|
||||
} else {
|
||||
// if the network is internal do not setup firewall rules
|
||||
if !self.info.network.internal {
|
||||
self.setup_firewall(data)?
|
||||
}
|
||||
}
|
||||
if let Some(w) = sysctl_writer {
|
||||
w.commit();
|
||||
}
|
||||
|
||||
Ok((response, aardvark_entry))
|
||||
}
|
||||
|
@ -352,6 +336,19 @@ impl driver::NetworkDriver for Bridge<'_> {
|
|||
};
|
||||
|
||||
if !self.info.network.internal && mode == BridgeMode::Managed {
|
||||
if complete_teardown {
|
||||
// delete sysctl file as well
|
||||
let path = sysctl::get_bridge_sysctl_d_path(&bridge_name);
|
||||
if let Err(e) = fs::remove_file(&path) {
|
||||
if e.kind() != std::io::ErrorKind::NotFound {
|
||||
error_list.push(NetavarkError::wrap(
|
||||
format!("failed to remove {path}"),
|
||||
e.into(),
|
||||
));
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
match self.teardown_firewall(complete_teardown, bridge_name) {
|
||||
Ok(_) => {}
|
||||
Err(err) => {
|
||||
|
@ -471,19 +468,6 @@ impl<'a> Bridge<'a> {
|
|||
|
||||
self.info.firewall.setup_network(sn, &system_dbus)?;
|
||||
|
||||
if spf.port_mappings.is_some() {
|
||||
// Need to enable sysctl localnet so that traffic can pass
|
||||
// through localhost to containers
|
||||
|
||||
CoreUtils::apply_sysctl_value(
|
||||
format!(
|
||||
"net.ipv4.conf.{}.route_localnet",
|
||||
data.bridge_interface_name
|
||||
),
|
||||
"1",
|
||||
)?;
|
||||
}
|
||||
|
||||
self.info.firewall.setup_port_forward(spf, &system_dbus)?;
|
||||
Ok(())
|
||||
}
|
||||
|
@ -502,7 +486,7 @@ impl<'a> Bridge<'a> {
|
|||
None => {
|
||||
let isolate = get_isolate_option(&self.info.network.options).unwrap_or_else(|e| {
|
||||
// just log we still try to do as much as possible for cleanup
|
||||
error!("failed to parse {} option: {}", OPTION_ISOLATE, e);
|
||||
error!("failed to parse {OPTION_ISOLATE} option: {e}");
|
||||
IsolateOption::Never
|
||||
});
|
||||
|
||||
|
@ -511,7 +495,7 @@ impl<'a> Bridge<'a> {
|
|||
Ok(i) => (i.container_addresses, i.nameservers),
|
||||
Err(e) => {
|
||||
// just log we still try to do as much as possible for cleanup
|
||||
error!("failed to parse ipam options: {}", e);
|
||||
error!("failed to parse ipam options: {e}");
|
||||
(Vec::new(), Vec::new())
|
||||
}
|
||||
};
|
||||
|
@ -557,40 +541,8 @@ impl<'a> Bridge<'a> {
|
|||
}
|
||||
|
||||
// sysctl forward
|
||||
|
||||
static IPV4_FORWARD_ONCE: Once = Once::new();
|
||||
static IPV6_FORWARD_ONCE: Once = Once::new();
|
||||
|
||||
const IPV4_FORWARD: &str = "net.ipv4.ip_forward";
|
||||
const IPV6_FORWARD: &str = "net.ipv6.conf.all.forwarding";
|
||||
|
||||
fn setup_ipv4_fw_sysctl() -> NetavarkResult<()> {
|
||||
let mut result = Ok("".to_string());
|
||||
|
||||
IPV4_FORWARD_ONCE.call_once(|| {
|
||||
result = CoreUtils::apply_sysctl_value(IPV4_FORWARD, "1");
|
||||
});
|
||||
|
||||
match result {
|
||||
Ok(_) => {}
|
||||
Err(e) => return Err(e.into()),
|
||||
};
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn setup_ipv6_fw_sysctl() -> NetavarkResult<()> {
|
||||
let mut result = Ok("".to_string());
|
||||
|
||||
IPV6_FORWARD_ONCE.call_once(|| {
|
||||
result = CoreUtils::apply_sysctl_value(IPV6_FORWARD, "1");
|
||||
});
|
||||
|
||||
match result {
|
||||
Ok(_) => {}
|
||||
Err(e) => return Err(e.into()),
|
||||
};
|
||||
Ok(())
|
||||
}
|
||||
const IPV4_FORWARD: &str = "net/ipv4/ip_forward";
|
||||
const IPV6_FORWARD: &str = "net/ipv6/conf/all/forwarding";
|
||||
|
||||
/// returns the container veth mac address
|
||||
fn create_interfaces(
|
||||
|
@ -598,21 +550,30 @@ fn create_interfaces(
|
|||
netns: &mut netlink::Socket,
|
||||
data: &InternalData,
|
||||
internal: bool,
|
||||
rootless: bool,
|
||||
hostns_fd: BorrowedFd<'_>,
|
||||
netns_fd: BorrowedFd<'_>,
|
||||
) -> NetavarkResult<String> {
|
||||
let (bridge_index, mac) = match host.get_link(netlink::LinkID::Name(
|
||||
) -> NetavarkResult<(
|
||||
String,
|
||||
Option<sysctl::SysctlDWriter<'static, String, String>>,
|
||||
)> {
|
||||
let mut sysctl_writer = None;
|
||||
let (bridge_index, mtu, mac) = match host.get_link(netlink::LinkID::Name(
|
||||
data.bridge_interface_name.to_string(),
|
||||
)) {
|
||||
Ok(bridge) => (
|
||||
validate_bridge_link(
|
||||
Ok(bridge) => {
|
||||
let (bridge_index, mtu) = validate_bridge_link(
|
||||
bridge,
|
||||
data.vlan.is_some(),
|
||||
host,
|
||||
&data.bridge_interface_name,
|
||||
)?,
|
||||
None,
|
||||
),
|
||||
)?;
|
||||
(
|
||||
bridge_index,
|
||||
if data.mtu == 0 { mtu } else { data.mtu },
|
||||
None,
|
||||
)
|
||||
}
|
||||
Err(err) => match err.unwrap() {
|
||||
NetavarkError::Netlink(e) => {
|
||||
if -e.raw_code() != libc::ENODEV {
|
||||
|
@ -626,10 +587,91 @@ fn create_interfaces(
|
|||
.wrap("in unmanaged mode, the bridge must already exist on the host");
|
||||
}
|
||||
|
||||
// Create sysctl writer, when using systemd and running as root it is possible that the
|
||||
// global configured sysctl.d config conflict with the values we write. In such cases
|
||||
// systemd-sysctl might overright the value causing hard to find errors:
|
||||
// https://issues.redhat.com/browse/RHEL-89477
|
||||
// This writer is used to generate a matching sysctl.d config file for our values so
|
||||
// systemd-sysctl is aware of them.
|
||||
let path = if is_using_systemd() && !rootless {
|
||||
let _ = fs::create_dir("/run/sysctl.d");
|
||||
Some(sysctl::get_bridge_sysctl_d_path(
|
||||
&data.bridge_interface_name,
|
||||
))
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
let mut sysctls = Vec::with_capacity(6);
|
||||
|
||||
// if internal block routing on the bridge otherwise enable routing globally
|
||||
if internal {
|
||||
sysctls.push((
|
||||
format!("net/ipv4/conf/{}/forwarding", data.bridge_interface_name),
|
||||
"0",
|
||||
));
|
||||
if data.ipam.ipv6_enabled {
|
||||
sysctls.push((
|
||||
format!("net/ipv6/conf/{}/forwarding", data.bridge_interface_name),
|
||||
"0",
|
||||
));
|
||||
}
|
||||
} else {
|
||||
sysctls.push((IPV4_FORWARD.to_string(), "1"));
|
||||
if data.ipam.ipv6_enabled {
|
||||
sysctls.push((IPV6_FORWARD.to_string(), "1"));
|
||||
}
|
||||
sysctls.push((
|
||||
format!(
|
||||
"net/ipv4/conf/{}/route_localnet",
|
||||
data.bridge_interface_name
|
||||
),
|
||||
"1",
|
||||
));
|
||||
}
|
||||
|
||||
if data.ipam.ipv6_enabled {
|
||||
// Disable duplicate address detection if ipv6 enabled
|
||||
// Do not accept Router Advertisements if ipv6 is enabled
|
||||
let br_accept_dad =
|
||||
format!("net/ipv6/conf/{}/accept_dad", &data.bridge_interface_name);
|
||||
let br_accept_ra =
|
||||
format!("net/ipv6/conf/{}/accept_ra", &data.bridge_interface_name);
|
||||
sysctls.push((br_accept_dad, "0"));
|
||||
sysctls.push((br_accept_ra, "0"));
|
||||
}
|
||||
|
||||
// Disable strict reverse path search validation. On RHEL it is set to strict mode
|
||||
// which breaks port forwarding when multiple networks are attached as the package
|
||||
// may be routed over a different interface on the reverse path.
|
||||
// As documented for the sysctl for complicated or asymmetric routing loose mode (2)
|
||||
// is recommended.
|
||||
let br_rp_filter =
|
||||
format!("net/ipv4/conf/{}/rp_filter", &data.bridge_interface_name);
|
||||
sysctls.push((br_rp_filter, "2"));
|
||||
|
||||
// writer must be create before the bridge is created
|
||||
let sw = sysctl::SysctlDWriter::new(path, sysctls);
|
||||
|
||||
let mut create_link_opts = netlink::CreateLinkOptions::new(
|
||||
data.bridge_interface_name.to_string(),
|
||||
InfoKind::Bridge,
|
||||
);
|
||||
|
||||
let mut mtu = data.mtu;
|
||||
if mtu == 0 {
|
||||
// if we have a default route, use its mtu as default
|
||||
if let Ok(iface_name) = get_default_route_interface(host) {
|
||||
match core_utils::get_mtu_from_iface(host, &iface_name) {
|
||||
Ok(iface_mtu) => {
|
||||
mtu = iface_mtu;
|
||||
},
|
||||
Err(e) => debug!(
|
||||
"failed to get mtu for default interface {iface_name}: {e}, using kernel default",
|
||||
),
|
||||
}
|
||||
}
|
||||
}
|
||||
create_link_opts.mtu = data.mtu;
|
||||
|
||||
if data.vlan.is_some() {
|
||||
|
@ -647,29 +689,9 @@ fn create_interfaces(
|
|||
|
||||
host.create_link(create_link_opts).wrap("create bridge")?;
|
||||
|
||||
if data.ipam.ipv6_enabled {
|
||||
// Disable duplicate address detection if ipv6 enabled
|
||||
// Do not accept Router Advertisements if ipv6 is enabled
|
||||
let br_accept_dad = format!(
|
||||
"/proc/sys/net/ipv6/conf/{}/accept_dad",
|
||||
&data.bridge_interface_name
|
||||
);
|
||||
let br_accept_ra =
|
||||
format!("net/ipv6/conf/{}/accept_ra", &data.bridge_interface_name);
|
||||
CoreUtils::apply_sysctl_value(br_accept_dad, "0")?;
|
||||
CoreUtils::apply_sysctl_value(br_accept_ra, "0")?;
|
||||
}
|
||||
|
||||
// Disable strict reverse path search validation. On RHEL it is set to strict mode
|
||||
// which breaks port forwarding when multiple networks are attached as the package
|
||||
// may be routed over a different interface on the reverse path.
|
||||
// As documented for the sysctl for complicated or asymmetric routing loose mode (2)
|
||||
// is recommended.
|
||||
let br_rp_filter = format!(
|
||||
"/proc/sys/net/ipv4/conf/{}/rp_filter",
|
||||
&data.bridge_interface_name
|
||||
);
|
||||
CoreUtils::apply_sysctl_value(br_rp_filter, "2")?;
|
||||
// Note sysctls must be written after the bridge is created
|
||||
sw.write_sysctls()?;
|
||||
sysctl_writer = Some(sw);
|
||||
|
||||
let link = host
|
||||
.get_link(netlink::LinkID::Name(
|
||||
|
@ -697,13 +719,13 @@ fn create_interfaces(
|
|||
host.set_up(netlink::LinkID::ID(link.header.index))
|
||||
.wrap("set bridge up")?;
|
||||
|
||||
(link.header.index, mac)
|
||||
(link.header.index, mtu, mac)
|
||||
}
|
||||
_ => return Err(err),
|
||||
},
|
||||
};
|
||||
|
||||
create_veth_pair(
|
||||
let mac = create_veth_pair(
|
||||
host,
|
||||
netns,
|
||||
data,
|
||||
|
@ -712,7 +734,9 @@ fn create_interfaces(
|
|||
internal,
|
||||
hostns_fd,
|
||||
netns_fd,
|
||||
)
|
||||
mtu,
|
||||
)?;
|
||||
Ok((mac, sysctl_writer))
|
||||
}
|
||||
|
||||
/// return the container veth mac address
|
||||
|
@ -726,11 +750,12 @@ fn create_veth_pair<'fd>(
|
|||
internal: bool,
|
||||
hostns_fd: BorrowedFd<'fd>,
|
||||
netns_fd: BorrowedFd<'fd>,
|
||||
mtu: u32,
|
||||
) -> NetavarkResult<String> {
|
||||
let mut peer_opts =
|
||||
netlink::CreateLinkOptions::new(data.container_interface_name.to_string(), InfoKind::Veth);
|
||||
peer_opts.mac = data.mac_address.clone().unwrap_or_default();
|
||||
peer_opts.mtu = data.mtu;
|
||||
peer_opts.mtu = mtu;
|
||||
peer_opts.netns = Some(netns_fd);
|
||||
|
||||
let mut peer = LinkMessage::default();
|
||||
|
@ -738,7 +763,7 @@ fn create_veth_pair<'fd>(
|
|||
|
||||
let mut host_veth =
|
||||
netlink::CreateLinkOptions::new(data.host_interface_name.clone(), InfoKind::Veth);
|
||||
host_veth.mtu = data.mtu;
|
||||
host_veth.mtu = mtu;
|
||||
host_veth.primary_index = primary_index;
|
||||
host_veth.info_data = Some(InfoData::Veth(InfoVeth::Peer(peer)));
|
||||
|
||||
|
@ -798,23 +823,20 @@ fn create_veth_pair<'fd>(
|
|||
if data.ipam.ipv6_enabled {
|
||||
// Disable dad inside the container too
|
||||
let disable_dad_in_container = format!(
|
||||
"/proc/sys/net/ipv6/conf/{}/accept_dad",
|
||||
"net/ipv6/conf/{}/accept_dad",
|
||||
&data.container_interface_name
|
||||
);
|
||||
core_utils::CoreUtils::apply_sysctl_value(disable_dad_in_container, "0")?;
|
||||
sysctl::apply_sysctl_value(disable_dad_in_container, "0")?;
|
||||
}
|
||||
let enable_arp_notify = format!(
|
||||
"/proc/sys/net/ipv4/conf/{}/arp_notify",
|
||||
"net/ipv4/conf/{}/arp_notify",
|
||||
&data.container_interface_name
|
||||
);
|
||||
core_utils::CoreUtils::apply_sysctl_value(enable_arp_notify, "1")?;
|
||||
sysctl::apply_sysctl_value(enable_arp_notify, "1")?;
|
||||
|
||||
// disable strict reverse path search validation
|
||||
let rp_filter = format!(
|
||||
"/proc/sys/net/ipv4/conf/{}/rp_filter",
|
||||
&data.container_interface_name
|
||||
);
|
||||
CoreUtils::apply_sysctl_value(rp_filter, "2")?;
|
||||
let rp_filter = format!("net/ipv4/conf/{}/rp_filter", &data.container_interface_name);
|
||||
sysctl::apply_sysctl_value(rp_filter, "2")?;
|
||||
Ok::<(), NetavarkError>(())
|
||||
});
|
||||
// check the result and return error
|
||||
|
@ -826,9 +848,8 @@ fn create_veth_pair<'fd>(
|
|||
for nla in host_veth.attributes.into_iter() {
|
||||
if let LinkAttribute::IfName(name) = nla {
|
||||
// Disable dad inside on the host too
|
||||
let disable_dad_in_container =
|
||||
format!("/proc/sys/net/ipv6/conf/{name}/accept_dad");
|
||||
core_utils::CoreUtils::apply_sysctl_value(disable_dad_in_container, "0")?;
|
||||
let disable_dad_in_container = format!("net/ipv6/conf/{name}/accept_dad");
|
||||
sysctl::apply_sysctl_value(disable_dad_in_container, "0")?;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -881,8 +902,13 @@ fn validate_bridge_link(
|
|||
vlan: bool,
|
||||
netlink: &mut netlink::Socket,
|
||||
br_name: &str,
|
||||
) -> NetavarkResult<u32> {
|
||||
) -> NetavarkResult<(u32, u32)> {
|
||||
let mut mtu: u32 = 0;
|
||||
let mut header_index: u32 = 0;
|
||||
for nla in msg.attributes.iter() {
|
||||
if let LinkAttribute::Mtu(m) = nla {
|
||||
mtu = *m;
|
||||
}
|
||||
if let LinkAttribute::LinkInfo(info) = nla {
|
||||
// when vlan is requested also check the VlanFiltering attribute
|
||||
if vlan {
|
||||
|
@ -920,7 +946,8 @@ fn validate_bridge_link(
|
|||
for inf in info.iter() {
|
||||
if let LinkInfo::Kind(kind) = inf {
|
||||
if *kind == InfoKind::Bridge {
|
||||
return Ok(msg.header.index);
|
||||
header_index = msg.header.index;
|
||||
break;
|
||||
} else {
|
||||
return Err(NetavarkError::Message(format!(
|
||||
"bridge interface {br_name} already exists but is a {kind:?} interface"
|
||||
|
@ -930,6 +957,11 @@ fn validate_bridge_link(
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
if header_index != 0 {
|
||||
return Ok((header_index, mtu));
|
||||
}
|
||||
|
||||
Err(NetavarkError::Message(format!(
|
||||
"could not determine namespace link kind for bridge {br_name}"
|
||||
)))
|
||||
|
@ -945,8 +977,7 @@ fn check_link_is_vrf(msg: LinkMessage, vrf_name: &str) -> NetavarkResult<LinkMes
|
|||
return Ok(msg);
|
||||
} else {
|
||||
return Err(NetavarkError::Message(format!(
|
||||
"vrf {} already exists but is a {:?} interface",
|
||||
vrf_name, kind
|
||||
"vrf {vrf_name} already exists but is a {kind:?} interface"
|
||||
)));
|
||||
}
|
||||
}
|
||||
|
@ -954,8 +985,7 @@ fn check_link_is_vrf(msg: LinkMessage, vrf_name: &str) -> NetavarkResult<LinkMes
|
|||
}
|
||||
}
|
||||
Err(NetavarkError::Message(format!(
|
||||
"could not determine namespace link kind for vrf {}",
|
||||
vrf_name
|
||||
"could not determine namespace link kind for vrf {vrf_name}"
|
||||
)))
|
||||
}
|
||||
|
||||
|
@ -982,7 +1012,7 @@ fn remove_link(
|
|||
// no connected interfaces on that bridge we can remove it
|
||||
if links.is_empty() {
|
||||
if let BridgeMode::Managed = mode {
|
||||
log::info!("removing bridge {}", br_name);
|
||||
log::info!("removing bridge {br_name}");
|
||||
host.del_link(netlink::LinkID::ID(br.header.index))
|
||||
.wrap(format!("failed to delete bridge {container_veth_name}"))?;
|
||||
return Ok(true);
|
||||
|
@ -1016,9 +1046,6 @@ fn get_bridge_mode_from_string(mode: Option<&str>) -> NetavarkResult<BridgeMode>
|
|||
fn maybe_add_alias<'a>(names: &mut Vec<SafeString<'a>>, name: &'a str) {
|
||||
match name.try_into() {
|
||||
Ok(name) => names.push(name),
|
||||
Err(err) => log::warn!(
|
||||
"invalid network alias {:?}: {err}, ignoring this name",
|
||||
name
|
||||
),
|
||||
Err(err) => log::warn!("invalid network alias {name:?}: {err}, ignoring this name"),
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,7 +2,6 @@ use crate::error::{ErrorWrap, NetavarkError, NetavarkResult};
|
|||
use crate::network::{constants, internal_types, types};
|
||||
use crate::wrap;
|
||||
use ipnet::IpNet;
|
||||
use log::debug;
|
||||
use netlink_packet_route::link::{IpVlanMode, MacVlanMode};
|
||||
use nix::sched;
|
||||
use sha2::{Digest, Sha512};
|
||||
|
@ -10,13 +9,13 @@ use std::collections::HashMap;
|
|||
use std::env;
|
||||
use std::fmt::Display;
|
||||
use std::fs::File;
|
||||
use std::io::{self, Error};
|
||||
use std::io;
|
||||
use std::net::IpAddr;
|
||||
use std::net::Ipv4Addr;
|
||||
use std::net::Ipv6Addr;
|
||||
use std::os::unix::prelude::*;
|
||||
use std::path::Path;
|
||||
use std::str::FromStr;
|
||||
use sysctl::{Sysctl, SysctlError};
|
||||
|
||||
use super::netlink;
|
||||
|
||||
|
@ -64,7 +63,7 @@ where
|
|||
pub fn get_ipam_addresses<'a>(
|
||||
per_network_opts: &'a types::PerNetworkOptions,
|
||||
network: &'a types::Network,
|
||||
) -> Result<internal_types::IPAMAddresses, std::io::Error> {
|
||||
) -> NetavarkResult<internal_types::IPAMAddresses> {
|
||||
let addresses = match network
|
||||
.ipam_options
|
||||
.as_ref()
|
||||
|
@ -86,12 +85,7 @@ pub fn get_ipam_addresses<'a>(
|
|||
let mut nameservers: Vec<IpAddr> = Vec::new();
|
||||
|
||||
let static_ips = match per_network_opts.static_ips.as_ref() {
|
||||
None => {
|
||||
return Err(std::io::Error::new(
|
||||
std::io::ErrorKind::Other,
|
||||
"no static ips provided",
|
||||
))
|
||||
}
|
||||
None => return Err(NetavarkError::msg("no static ips provided")),
|
||||
Some(i) => i,
|
||||
};
|
||||
|
||||
|
@ -102,10 +96,9 @@ pub fn get_ipam_addresses<'a>(
|
|||
let gw_net = match ipnet::IpNet::new(gw, subnet_mask_cidr) {
|
||||
Ok(dest) => dest,
|
||||
Err(err) => {
|
||||
return Err(std::io::Error::new(
|
||||
std::io::ErrorKind::Other,
|
||||
format!("failed to parse address {gw}/{subnet_mask_cidr}: {err}"),
|
||||
))
|
||||
return Err(NetavarkError::msg(format!(
|
||||
"failed to parse address {gw}/{subnet_mask_cidr}: {err}"
|
||||
)))
|
||||
}
|
||||
};
|
||||
gateway_addresses.push(gw_net);
|
||||
|
@ -122,7 +115,7 @@ pub fn get_ipam_addresses<'a>(
|
|||
match format!("{}/{}", static_ips[idx], subnet_mask_cidr).parse() {
|
||||
Ok(i) => i,
|
||||
Err(e) => {
|
||||
return Err(Error::new(std::io::ErrorKind::Other, e));
|
||||
return Err(NetavarkError::SubnetParse(e));
|
||||
}
|
||||
};
|
||||
// Add the IP to the address_vector
|
||||
|
@ -136,7 +129,7 @@ pub fn get_ipam_addresses<'a>(
|
|||
let routes: Vec<netlink::Route> = match create_route_list(&network.routes) {
|
||||
Ok(r) => r,
|
||||
Err(e) => {
|
||||
return Err(Error::new(std::io::ErrorKind::Other, e));
|
||||
return Err(e);
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -172,10 +165,9 @@ pub fn get_ipam_addresses<'a>(
|
|||
nameservers: vec![],
|
||||
},
|
||||
Some(driver) => {
|
||||
return Err(std::io::Error::new(
|
||||
std::io::ErrorKind::Other,
|
||||
format!("unsupported ipam driver {driver}"),
|
||||
));
|
||||
return Err(NetavarkError::msg(format!(
|
||||
"unsupported ipam driver {driver}"
|
||||
)));
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -193,7 +185,7 @@ impl CoreUtils {
|
|||
address
|
||||
}
|
||||
|
||||
pub fn decode_address_from_hex(input: &str) -> Result<Vec<u8>, std::io::Error> {
|
||||
pub fn decode_address_from_hex(input: &str) -> NetavarkResult<Vec<u8>> {
|
||||
let bytes: Result<Vec<u8>, _> = input
|
||||
.split([':', '-'])
|
||||
.map(|b| u8::from_str_radix(b, 16))
|
||||
|
@ -202,18 +194,16 @@ impl CoreUtils {
|
|||
let result = match bytes {
|
||||
Ok(bytes) => {
|
||||
if bytes.len() != 6 {
|
||||
return Err(std::io::Error::new(
|
||||
std::io::ErrorKind::Other,
|
||||
format!("invalid mac length for address: {input}"),
|
||||
));
|
||||
return Err(NetavarkError::msg(format!(
|
||||
"invalid mac length for address: {input}"
|
||||
)));
|
||||
}
|
||||
bytes
|
||||
}
|
||||
Err(e) => {
|
||||
return Err(std::io::Error::new(
|
||||
std::io::ErrorKind::Other,
|
||||
format!("unable to parse mac address {input}: {e}"),
|
||||
));
|
||||
return Err(NetavarkError::msg(format!(
|
||||
"unable to parse mac address {input}: {e}"
|
||||
)));
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -255,26 +245,6 @@ impl CoreUtils {
|
|||
let response = &hash_string[0..length];
|
||||
response.to_string()
|
||||
}
|
||||
|
||||
/// Set a sysctl value by value's namespace.
|
||||
pub fn apply_sysctl_value(
|
||||
ns_value: impl AsRef<str>,
|
||||
val: impl AsRef<str>,
|
||||
) -> Result<String, SysctlError> {
|
||||
let ns_value = ns_value.as_ref();
|
||||
let val = val.as_ref();
|
||||
debug!("Setting sysctl value for {} to {}", ns_value, val);
|
||||
let ctl = sysctl::Ctl::new(ns_value)?;
|
||||
match ctl.value_string() {
|
||||
Ok(result) => {
|
||||
if result == val {
|
||||
return Ok(result);
|
||||
}
|
||||
}
|
||||
Err(e) => return Err(e),
|
||||
}
|
||||
ctl.set_value_string(val)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn join_netns<Fd: AsFd>(fd: Fd) -> NetavarkResult<()> {
|
||||
|
@ -413,31 +383,6 @@ pub fn create_route_list(
|
|||
}
|
||||
}
|
||||
|
||||
pub fn disable_ipv6_autoconf(if_name: &str) -> NetavarkResult<()> {
|
||||
// make sure autoconf is off, we want manual config only
|
||||
if let Err(err) =
|
||||
CoreUtils::apply_sysctl_value(format!("/proc/sys/net/ipv6/conf/{if_name}/autoconf"), "0")
|
||||
{
|
||||
match err {
|
||||
SysctlError::NotFound(_) => {
|
||||
// if the sysctl is not found we likely run on a system without ipv6
|
||||
// just ignore that case
|
||||
}
|
||||
|
||||
// if we have a read only /proc we ignore it as well
|
||||
SysctlError::IoError(ref e) if e.raw_os_error() == Some(libc::EROFS) => {}
|
||||
|
||||
_ => {
|
||||
return Err(NetavarkError::wrap(
|
||||
"failed to set autoconf sysctl",
|
||||
NetavarkError::Sysctl(err),
|
||||
));
|
||||
}
|
||||
}
|
||||
};
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn get_mac_address(v: Vec<LinkAttribute>) -> NetavarkResult<String> {
|
||||
for nla in v.into_iter() {
|
||||
if let LinkAttribute::Address(ref addr) = nla {
|
||||
|
@ -448,3 +393,54 @@ pub fn get_mac_address(v: Vec<LinkAttribute>) -> NetavarkResult<String> {
|
|||
"failed to get the the container mac address",
|
||||
))
|
||||
}
|
||||
|
||||
/// check if systemd is booted, see sd_booted(3)
|
||||
pub fn is_using_systemd() -> bool {
|
||||
Path::new("/run/systemd/system").exists()
|
||||
}
|
||||
|
||||
pub fn get_default_route_interface(host: &mut netlink::Socket) -> NetavarkResult<String> {
|
||||
let routes = host.dump_routes().wrap("dump routes")?;
|
||||
|
||||
for route in routes {
|
||||
let mut dest = false;
|
||||
let mut out_if = 0;
|
||||
for nla in route.attributes {
|
||||
if let netlink_packet_route::route::RouteAttribute::Destination(_) = nla {
|
||||
dest = true;
|
||||
}
|
||||
if let netlink_packet_route::route::RouteAttribute::Oif(oif) = nla {
|
||||
out_if = oif;
|
||||
}
|
||||
}
|
||||
|
||||
// if there is no dest we have a default route
|
||||
// return the output interface for this route
|
||||
if !dest && out_if > 0 {
|
||||
let link = host.get_link(netlink::LinkID::ID(out_if))?;
|
||||
let name = link.attributes.iter().find_map(|nla| {
|
||||
if let LinkAttribute::IfName(name) = nla {
|
||||
Some(name)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
});
|
||||
if let Some(name) = name {
|
||||
return Ok(name.to_owned());
|
||||
}
|
||||
}
|
||||
}
|
||||
Err(NetavarkError::msg("failed to get default route interface"))
|
||||
}
|
||||
|
||||
pub fn get_mtu_from_iface(host: &mut netlink::Socket, iface_name: &str) -> NetavarkResult<u32> {
|
||||
let link = host.get_link(netlink::LinkID::Name(iface_name.to_string()))?;
|
||||
for nla in link.attributes.iter() {
|
||||
if let LinkAttribute::Mtu(mtu) = nla {
|
||||
return Ok(*mtu);
|
||||
}
|
||||
}
|
||||
// It is possible that the interface has no MTU set, in this case the kernel will use the default.
|
||||
// We return 0 to signal this, which netavark uses to mean "kernel default".
|
||||
Ok(0)
|
||||
}
|
||||
|
|
|
@ -18,6 +18,7 @@ pub mod driver;
|
|||
pub mod internal_types;
|
||||
pub mod netlink;
|
||||
pub mod plugin;
|
||||
pub mod sysctl;
|
||||
pub mod vlan;
|
||||
|
||||
impl types::NetworkOptions {
|
||||
|
|
|
@ -337,7 +337,7 @@ impl Socket {
|
|||
|
||||
pub fn add_route(&mut self, route: &Route) -> NetavarkResult<()> {
|
||||
let msg = Self::create_route_msg(route);
|
||||
info!("Adding route {}", route);
|
||||
info!("Adding route {route}");
|
||||
|
||||
let result = self
|
||||
.make_netlink_request(RouteNetlinkMessage::NewRoute(msg), NLM_F_ACK | NLM_F_CREATE)?;
|
||||
|
@ -348,7 +348,7 @@ impl Socket {
|
|||
|
||||
pub fn del_route(&mut self, route: &Route) -> NetavarkResult<()> {
|
||||
let msg = Self::create_route_msg(route);
|
||||
info!("Deleting route {}", route);
|
||||
info!("Deleting route {route}");
|
||||
|
||||
let result = self.make_netlink_request(RouteNetlinkMessage::DelRoute(msg), NLM_F_ACK)?;
|
||||
expect_netlink_result!(result, 0);
|
||||
|
@ -486,7 +486,7 @@ impl Socket {
|
|||
packet.finalize();
|
||||
|
||||
packet.serialize(&mut self.buffer[..]);
|
||||
trace!("send netlink packet: {:?}", packet);
|
||||
trace!("send netlink packet: {packet:?}");
|
||||
|
||||
self.socket.send(&self.buffer[..packet.buffer_len()], 0)?;
|
||||
Ok(())
|
||||
|
@ -511,7 +511,7 @@ impl Socket {
|
|||
"failed to deserialize netlink message: {e}",
|
||||
))
|
||||
})?;
|
||||
trace!("read netlink packet: {:?}", rx_packet);
|
||||
trace!("read netlink packet: {rx_packet:?}");
|
||||
|
||||
if rx_packet.header.sequence_number != self.sequence_number {
|
||||
return Err(NetavarkError::msg(format!(
|
||||
|
|
|
@ -0,0 +1,184 @@
|
|||
use std::{
|
||||
fs::{self, File, OpenOptions},
|
||||
io::{ErrorKind, Read as _, Result, Write as _},
|
||||
path::Path,
|
||||
};
|
||||
|
||||
use crate::error::{NetavarkError, NetavarkResult};
|
||||
|
||||
pub fn apply_sysctl_value(ns_value: impl AsRef<str>, val: impl AsRef<str>) -> NetavarkResult<()> {
|
||||
_apply_sysctl_value(&ns_value, val)
|
||||
.map_err(|e| NetavarkError::wrap(format!("set sysctl {}", ns_value.as_ref()), e.into()))
|
||||
}
|
||||
|
||||
/// Set a sysctl value by value's namespace.
|
||||
/// ns_value is the path of the sysctl (using slashes not dots!) and without the "/proc/sys/" prefix.
|
||||
fn _apply_sysctl_value(ns_value: impl AsRef<str>, val: impl AsRef<str>) -> Result<()> {
|
||||
const PREFIX: &str = "/proc/sys/";
|
||||
let ns_value = ns_value.as_ref();
|
||||
let mut path = String::with_capacity(PREFIX.len() + ns_value.len());
|
||||
path.push_str(PREFIX);
|
||||
path.push_str(ns_value);
|
||||
let val = val.as_ref();
|
||||
|
||||
log::debug!("Setting sysctl value for {ns_value} to {val}");
|
||||
|
||||
let mut f = File::open(&path)?;
|
||||
let mut buf = String::with_capacity(1);
|
||||
f.read_to_string(&mut buf)?;
|
||||
|
||||
if buf.trim() == val {
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
let mut f = OpenOptions::new().write(true).open(&path)?;
|
||||
f.write_all(val.as_bytes())
|
||||
}
|
||||
|
||||
pub fn disable_ipv6_autoconf(if_name: &str) -> NetavarkResult<()> {
|
||||
// make sure autoconf is off, we want manual config only
|
||||
if let Err(err) = _apply_sysctl_value(format!("net/ipv6/conf/{if_name}/autoconf"), "0") {
|
||||
match err.kind() {
|
||||
ErrorKind::NotFound => {
|
||||
// if the sysctl is not found we likely run on a system without ipv6
|
||||
// just ignore that case
|
||||
}
|
||||
|
||||
// if we have a read only /proc we ignore it as well
|
||||
ErrorKind::ReadOnlyFilesystem => {}
|
||||
|
||||
_ => {
|
||||
return Err(NetavarkError::wrap(
|
||||
"failed to set autoconf sysctl",
|
||||
err.into(),
|
||||
));
|
||||
}
|
||||
}
|
||||
};
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn get_bridge_sysctl_d_path(bridge_name: &str) -> String {
|
||||
format!("/run/sysctl.d/10-netavark-{bridge_name}.conf")
|
||||
}
|
||||
|
||||
pub struct SysctlDWriter<'a, P: AsRef<Path>, V: AsRef<str> + 'a> {
|
||||
path: Option<P>,
|
||||
sysctls: Vec<(V, &'a str)>,
|
||||
commit: bool,
|
||||
}
|
||||
|
||||
impl<'a, P: AsRef<Path>, V: AsRef<str> + 'a> SysctlDWriter<'a, P, V> {
|
||||
/// Create a new sysctl writer for the given file path and sysctls values.
|
||||
/// When path is not None is set it will write a sysctl.d(5) file to the path.
|
||||
///
|
||||
/// Note SysctlDWriter should be created before any interfaces that are referenced
|
||||
/// in the given sysctls. The reason is systemd will read the file and automatically
|
||||
/// apply the values as oon as the interface is created.
|
||||
/// If the config file is created after the interface there is the race condition in
|
||||
/// which systemd-sysctl might not read the confif file and writes the old values.
|
||||
/// And that then races against the write_sysctls() call.
|
||||
///
|
||||
/// Also write_sysctls() is separate because we can only directly write to /proc file
|
||||
/// once the interface exist so we need two steps.
|
||||
///
|
||||
/// Important one must call commit() otherwise we remove the file again on drop.
|
||||
pub fn new(path: Option<P>, sysctls: Vec<(V, &'a str)>) -> Self {
|
||||
if let Some(path) = &path {
|
||||
// Note using create_new here as we don't want to overwrite an existing file
|
||||
// If such file already exists it should be safe to assume it has the right
|
||||
// content so we can skip writing it again.
|
||||
let _ = File::create_new(path).and_then(|mut f| {
|
||||
if Self::write_file(&mut f, &sysctls).is_err() {
|
||||
// remove file again on write errors
|
||||
fs::remove_file(path)
|
||||
} else {
|
||||
Ok(())
|
||||
}
|
||||
});
|
||||
}
|
||||
Self {
|
||||
path,
|
||||
sysctls,
|
||||
commit: false,
|
||||
}
|
||||
}
|
||||
|
||||
/// write the given sysctl in format according to sysctl.d(5)
|
||||
fn write_file(f: &mut File, sysctls: &[(V, &'a str)]) -> Result<()> {
|
||||
let mut buf = String::with_capacity(1024);
|
||||
buf.push_str("# autogenerated by netavark\n");
|
||||
for (key, val) in sysctls {
|
||||
buf.push_str(key.as_ref());
|
||||
buf.push_str(" = ");
|
||||
buf.push_str(val);
|
||||
buf.push('\n');
|
||||
}
|
||||
f.write_all(buf.as_bytes())
|
||||
}
|
||||
|
||||
// write the sysctls to /proc/sys/
|
||||
pub fn write_sysctls(&self) -> NetavarkResult<()> {
|
||||
for (key, val) in &self.sysctls {
|
||||
apply_sysctl_value(key, val)?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// mark as successfully written so the file is not removed on drop
|
||||
/// The reason we do this is to avoid manually removing the file on
|
||||
/// each early exit due errors.
|
||||
/// This also consumes the writer so it is impossible to keep writing
|
||||
/// after this is called.
|
||||
pub fn commit(mut self) {
|
||||
self.commit = true;
|
||||
}
|
||||
}
|
||||
|
||||
impl<P: AsRef<Path>, V: AsRef<str>> Drop for SysctlDWriter<'_, P, V> {
|
||||
fn drop(&mut self) {
|
||||
if self.commit {
|
||||
// do nothing
|
||||
return;
|
||||
}
|
||||
if let Some(p) = &self.path {
|
||||
// ignore errors here
|
||||
let _ = fs::remove_file(p);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use tempfile::Builder;
|
||||
|
||||
#[test]
|
||||
fn test_sysctl_d_writer() {
|
||||
let tmp_dir = Builder::new()
|
||||
.prefix("example")
|
||||
.tempdir()
|
||||
.expect("failed to create tmpdir");
|
||||
let filepath = tmp_dir.path().join("test.conf");
|
||||
let w = SysctlDWriter::new(Some(&filepath), vec![("abc", "1"), ("def", "2")]);
|
||||
w.commit();
|
||||
let res = fs::read_to_string(filepath).expect("failed to read file");
|
||||
assert_eq!(res, "# autogenerated by netavark\nabc = 1\ndef = 2\n");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_sysctl_d_writer_nocommit() {
|
||||
let tmp_dir = Builder::new()
|
||||
.prefix("example")
|
||||
.tempdir()
|
||||
.expect("failed to create tmpdir");
|
||||
let filepath = tmp_dir.path().join("test.conf");
|
||||
let w = SysctlDWriter::new(Some(&filepath), vec![("abc", "1"), ("def", "2")]);
|
||||
// drop the writer here which should delete the file
|
||||
drop(w);
|
||||
assert!(
|
||||
!filepath.exists(),
|
||||
"file should not exist after drop without commit()"
|
||||
)
|
||||
}
|
||||
}
|
|
@ -3,16 +3,18 @@ use std::os::fd::BorrowedFd;
|
|||
use std::{collections::HashMap, net::IpAddr};
|
||||
|
||||
use netlink_packet_route::link::{
|
||||
InfoData, InfoIpVlan, InfoKind, InfoMacVlan, IpVlanMode, LinkAttribute, MacVlanMode,
|
||||
InfoData, InfoIpVlan, InfoKind, InfoMacVlan, IpVlanMode, MacVlanMode,
|
||||
};
|
||||
use rand::distr::{Alphanumeric, SampleString};
|
||||
|
||||
use crate::network::core_utils::get_default_route_interface;
|
||||
use crate::network::dhcp::{dhcp_teardown, get_dhcp_lease};
|
||||
use crate::{
|
||||
dns::aardvark::AardvarkEntry,
|
||||
error::{ErrorWrap, NetavarkError, NetavarkResult},
|
||||
exec_netns,
|
||||
network::core_utils::{disable_ipv6_autoconf, join_netns},
|
||||
network::core_utils::join_netns,
|
||||
network::sysctl::disable_ipv6_autoconf,
|
||||
};
|
||||
|
||||
use super::{
|
||||
|
@ -317,10 +319,7 @@ fn setup(
|
|||
// make sure to delete the tmp interface.
|
||||
if let Err(err) = netns.del_link(netlink::LinkID::ID(link.header.index))
|
||||
{
|
||||
error!(
|
||||
"failed to delete tmp {} link {}: {}",
|
||||
kind_data, tmp_name, err
|
||||
);
|
||||
error!("failed to delete tmp {kind_data} link {tmp_name}: {err}");
|
||||
};
|
||||
})?;
|
||||
|
||||
|
@ -360,37 +359,3 @@ fn setup(
|
|||
|
||||
get_mac_address(dev.attributes)
|
||||
}
|
||||
|
||||
fn get_default_route_interface(host: &mut netlink::Socket) -> NetavarkResult<String> {
|
||||
let routes = host.dump_routes().wrap("dump routes")?;
|
||||
|
||||
for route in routes {
|
||||
let mut dest = false;
|
||||
let mut out_if = 0;
|
||||
for nla in route.attributes {
|
||||
if let netlink_packet_route::route::RouteAttribute::Destination(_) = nla {
|
||||
dest = true;
|
||||
}
|
||||
if let netlink_packet_route::route::RouteAttribute::Oif(oif) = nla {
|
||||
out_if = oif;
|
||||
}
|
||||
}
|
||||
|
||||
// if there is no dest we have a default route
|
||||
// return the output interface for this route
|
||||
if !dest && out_if > 0 {
|
||||
let link = host.get_link(netlink::LinkID::ID(out_if))?;
|
||||
let name = link.attributes.iter().find_map(|nla| {
|
||||
if let LinkAttribute::IfName(name) = nla {
|
||||
Some(name)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
});
|
||||
if let Some(name) = name {
|
||||
return Ok(name.to_owned());
|
||||
}
|
||||
}
|
||||
}
|
||||
Err(NetavarkError::msg("failed to get default route interface"))
|
||||
}
|
||||
|
|
|
@ -49,7 +49,7 @@ EOF
|
|||
assert `grep -c "client provides name" "$TMP_TESTDIR/dnsmasq.log"` == 0
|
||||
}
|
||||
|
||||
@test "empty interface should fail 155" {
|
||||
@test "empty interface should fail" {
|
||||
read -r -d '\0' input_config <<EOF
|
||||
{
|
||||
"container_iface": "",
|
||||
|
@ -63,12 +63,12 @@ EOF
|
|||
}
|
||||
\0
|
||||
EOF
|
||||
# Not providing an interface in the config should result
|
||||
# in an error and a return code of 156
|
||||
expected_rc=155 run_setup "$input_config"
|
||||
|
||||
expected_rc=1 run_setup "$input_config"
|
||||
assert "$output" =~ "No such device" "interface not found error"
|
||||
}
|
||||
|
||||
@test "empty mac address should fail 156" {
|
||||
@test "empty mac address should fail" {
|
||||
read -r -d '\0' input_config <<EOF
|
||||
{
|
||||
"container_iface": "veth0",
|
||||
|
@ -82,17 +82,17 @@ EOF
|
|||
}
|
||||
\0
|
||||
EOF
|
||||
# Not mac address should result in an error
|
||||
# and return code of 156
|
||||
expected_rc=156 run_setup "$input_config"
|
||||
|
||||
expected_rc=1 run_setup "$input_config"
|
||||
assert "$output" =~ "unable to parse mac address : cannot parse integer from empty string" "empty mac error"
|
||||
}
|
||||
|
||||
@test "invalid interface should fail 156" {
|
||||
@test "invalid interface should fail" {
|
||||
read -r -d '\0' input_config <<EOF
|
||||
{
|
||||
"container_iface": "veth990",
|
||||
"host_iface": "veth1",
|
||||
"container_mac_addr": "",
|
||||
"container_mac_addr": "$CONTAINER_MAC",
|
||||
"domain_name": "example.com",
|
||||
"host_name": "foobar",
|
||||
"version": 0,
|
||||
|
@ -101,12 +101,12 @@ EOF
|
|||
}
|
||||
\0
|
||||
EOF
|
||||
# A non-existent interface should result in an
|
||||
# error and return code of 156
|
||||
expected_rc=156 run_setup "$input_config"
|
||||
|
||||
expected_rc=1 run_setup "$input_config"
|
||||
assert "$output" =~ "No such device" "interface not found error"
|
||||
}
|
||||
|
||||
@test "invalid mac address should fail 156" {
|
||||
@test "invalid mac address should fail" {
|
||||
read -r -d '\0' input_config <<EOF
|
||||
{
|
||||
"container_iface": "veth0",
|
||||
|
@ -121,7 +121,7 @@ EOF
|
|||
\0
|
||||
EOF
|
||||
|
||||
# An invalid mac address should result in an
|
||||
# error and a return code of 156
|
||||
expected_rc=156 run_setup "$input_config"
|
||||
|
||||
expected_rc=1 run_setup "$input_config"
|
||||
assert "$output" =~ "unable to parse mac address 123" "mac address error"
|
||||
}
|
||||
|
|
|
@ -316,8 +316,6 @@ fw_driver=iptables
|
|||
|
||||
NETAVARK_DNS_PORT="$dns_port" run_netavark --file ${TESTSDIR}/testfiles/dualstack-bridge.json \
|
||||
setup $(get_container_netns_path)
|
||||
config="$output"
|
||||
assert_json "$config" ".podman1.dns_search_domains" == "[]" "empty search domains"
|
||||
|
||||
# check iptables
|
||||
run_in_host_netns iptables -t nat -S NETAVARK-HOSTPORT-DNAT
|
||||
|
@ -670,7 +668,7 @@ EOF
|
|||
EOF
|
||||
|
||||
expected_rc=1 run_netavark setup $(get_container_netns_path) <<<"$config"
|
||||
assert_json ".error" "IO error: unsupported ipam driver someDriver" "Driver is not supported error"
|
||||
assert_json ".error" "unsupported ipam driver someDriver" "Driver is not supported error"
|
||||
}
|
||||
|
||||
@test "$fw_driver - isolate networks" {
|
||||
|
@ -831,6 +829,7 @@ EOF
|
|||
run_in_host_netns sh -c "echo 1 > /proc/sys/net/ipv4/ip_forward"
|
||||
run_in_container_netns sh -c "echo 1 > /proc/sys/net/ipv4/conf/default/arp_notify"
|
||||
run_in_host_netns sh -c "echo 2 > /proc/sys/net/ipv4/conf/default/rp_filter"
|
||||
run_in_host_netns sh -c "echo 1 > /proc/sys/net/ipv4/conf/default/route_localnet"
|
||||
run_in_container_netns sh -c "echo 2 > /proc/sys/net/ipv4/conf/default/rp_filter"
|
||||
run_in_host_netns mount -t proc -o ro,nosuid,nodev,noexec proc /proc
|
||||
|
||||
|
@ -842,7 +841,7 @@ EOF
|
|||
run_in_host_netns mount -t proc -o remount,ro /proc
|
||||
|
||||
expected_rc=1 run_netavark --file ${TESTSDIR}/testfiles/simplebridge.json setup $(get_container_netns_path)
|
||||
assert_json ".error" "Sysctl error: IO Error: Read-only file system (os error 30)" "Sysctl error because fs is read only"
|
||||
assert_json ".error" "set sysctl net/ipv4/ip_forward: IO error: Read-only file system (os error 30)" "Sysctl error because fs is read only"
|
||||
}
|
||||
|
||||
|
||||
|
@ -1097,7 +1096,7 @@ function check_simple_bridge_iptables() {
|
|||
|
||||
@test "$fw_driver - aardvark-dns error cleanup" {
|
||||
expected_rc=1 run_netavark -a /usr/bin/false --file ${TESTSDIR}/testfiles/dualstack-bridge-custom-dns-server.json setup $(get_container_netns_path)
|
||||
assert_json ".error" "error while applying dns entries: IO error: aardvark-dns exited unexpectedly without error message" "aardvark-dns error"
|
||||
assert_json ".error" "error while applying dns entries: aardvark-dns exited unexpectedly without error message" "aardvark-dns error"
|
||||
run_in_host_netns iptables -S
|
||||
assert "$output" !~ "10.89.3.0/24" "leaked network iptables rules after setup error"
|
||||
run_in_host_netns iptables -S -t nat
|
||||
|
|
|
@ -162,8 +162,6 @@ function setup() {
|
|||
NETAVARK_DNS_PORT="$dns_port" \
|
||||
run_netavark --file ${TESTSDIR}/testfiles/dualstack-bridge.json \
|
||||
setup $(get_container_netns_path)
|
||||
config="$output"
|
||||
assert_json "$config" ".podman1.dns_search_domains" == "[]" "empty search domains"
|
||||
|
||||
# check iptables
|
||||
# firewall-cmd --list-rich-rules does not guarantee order, use sort
|
||||
|
|
|
@ -314,8 +314,6 @@ export NETAVARK_FW=nftables
|
|||
|
||||
NETAVARK_DNS_PORT="$dns_port" run_netavark --file ${TESTSDIR}/testfiles/dualstack-bridge.json \
|
||||
setup $(get_container_netns_path)
|
||||
config="$output"
|
||||
assert_json "$config" ".podman1.dns_search_domains" == "[]" "empty search domains"
|
||||
|
||||
# check nftables
|
||||
run_in_host_netns nft list chain inet netavark NETAVARK-HOSTPORT-DNAT
|
||||
|
@ -554,7 +552,7 @@ EOF
|
|||
EOF
|
||||
|
||||
expected_rc=1 run_netavark setup $(get_container_netns_path) <<<"$config"
|
||||
assert_json ".error" "IO error: unsupported ipam driver someDriver" "Driver is not supported error"
|
||||
assert_json ".error" "unsupported ipam driver someDriver" "Driver is not supported error"
|
||||
}
|
||||
|
||||
@test "$fw_driver - isolate networks" {
|
||||
|
@ -716,6 +714,7 @@ EOF
|
|||
run_in_host_netns sh -c "echo 1 > /proc/sys/net/ipv4/ip_forward"
|
||||
run_in_container_netns sh -c "echo 1 > /proc/sys/net/ipv4/conf/default/arp_notify"
|
||||
run_in_host_netns sh -c "echo 2 > /proc/sys/net/ipv4/conf/default/rp_filter"
|
||||
run_in_host_netns sh -c "echo 1 > /proc/sys/net/ipv4/conf/default/route_localnet"
|
||||
run_in_container_netns sh -c "echo 2 > /proc/sys/net/ipv4/conf/default/rp_filter"
|
||||
run_in_host_netns mount -t proc -o ro,nosuid,nodev,noexec proc /proc
|
||||
|
||||
|
@ -727,7 +726,7 @@ EOF
|
|||
run_in_host_netns mount -t proc -o remount,ro /proc
|
||||
|
||||
expected_rc=1 run_netavark --file ${TESTSDIR}/testfiles/simplebridge.json setup $(get_container_netns_path)
|
||||
assert_json ".error" "Sysctl error: IO Error: Read-only file system (os error 30)" "Sysctl error because fs is read only"
|
||||
assert_json ".error" "set sysctl net/ipv4/ip_forward: IO error: Read-only file system (os error 30)" "Sysctl error because fs is read only"
|
||||
}
|
||||
|
||||
|
||||
|
@ -828,12 +827,28 @@ EOF
|
|||
done
|
||||
|
||||
run_netavark setup $(get_container_netns_path) <<<"${configs[0]}"
|
||||
|
||||
local sysctl_content="# autogenerated by netavark
|
||||
net/ipv4/ip_forward = 1
|
||||
net/ipv4/conf/podman1/route_localnet = 1
|
||||
net/ipv4/conf/podman1/rp_filter = 2"
|
||||
run_in_host_netns cat /run/sysctl.d/10-netavark-podman1.conf
|
||||
assert "$output" == "$sysctl_content" "sysctl.d file content"
|
||||
|
||||
run_netavark setup $(get_container_netns_path 1) <<<"${configs[1]}"
|
||||
|
||||
# content must still be the same after second setup
|
||||
run_in_host_netns cat /run/sysctl.d/10-netavark-podman1.conf
|
||||
assert "$output" == "$sysctl_content" "sysctl.d file content"
|
||||
|
||||
run_netavark teardown $(get_container_netns_path) <<<"${configs[0]}"
|
||||
# bridge should still exist
|
||||
run_in_host_netns ip link show podman1
|
||||
|
||||
# after first teardown file must still exist as bridge is still active
|
||||
run_in_host_netns cat /run/sysctl.d/10-netavark-podman1.conf
|
||||
assert "$output" == "$sysctl_content" "sysctl.d file content"
|
||||
|
||||
# check nftables POSTROUTING chain
|
||||
run_in_host_netns nft list chain inet netavark POSTROUTING
|
||||
assert "${lines[3]}" =~ "meta mark & 0x00002000 == 0x00002000 masquerade" "Mark-masquerade rule"
|
||||
|
@ -858,6 +873,9 @@ EOF
|
|||
# bridge should be removed
|
||||
expected_rc=1 run_in_host_netns ip link show podman1
|
||||
|
||||
# after all bridge teardown file must be removed
|
||||
expected_rc=1 run_in_host_netns cat /run/sysctl.d/10-netavark-podman1.conf
|
||||
|
||||
run_in_host_netns nft list chain inet netavark FORWARD
|
||||
assert "${lines[3]}" =~ "ct state invalid drop" "forward rule 1"
|
||||
assert "${#lines[@]}" = 7 "too many NETAVARK_FORWARD rules"
|
||||
|
@ -1036,7 +1054,7 @@ function check_simple_bridge_nftables() {
|
|||
|
||||
@test "$fw_driver - aardvark-dns error cleanup" {
|
||||
expected_rc=1 run_netavark -a /usr/bin/false --file ${TESTSDIR}/testfiles/dualstack-bridge-custom-dns-server.json setup $(get_container_netns_path)
|
||||
assert_json ".error" "error while applying dns entries: IO error: aardvark-dns exited unexpectedly without error message" "aardvark-dns error"
|
||||
assert_json ".error" "error while applying dns entries: aardvark-dns exited unexpectedly without error message" "aardvark-dns error"
|
||||
|
||||
run_in_host_netns nft list table inet netavark
|
||||
assert "$output" !~ "10.89.3.0/24" "leaked network nft rules after setup error"
|
||||
|
|
|
@ -0,0 +1,68 @@
|
|||
#!/usr/bin/env bats -*- bats -*-
|
||||
#
|
||||
# bridge driver tests with explicit modes
|
||||
#
|
||||
|
||||
load helpers
|
||||
|
||||
function check_iface_mtu() {
|
||||
local host_or_container=$1
|
||||
local iface=$2
|
||||
local mtu=$3
|
||||
|
||||
if [ "$host_or_container" = "host" ]; then
|
||||
run_in_host_netns ip -j --details link show "$iface"
|
||||
else
|
||||
run_in_container_netns ip -j --details link show "$iface"
|
||||
fi
|
||||
assert_json "$output" ".[].mtu" "==" "$mtu" "$iface MTU matches $mtu"
|
||||
}
|
||||
|
||||
function check_mtu() {
|
||||
local mtu=$1
|
||||
check_iface_mtu container eth0 "$mtu"
|
||||
check_iface_mtu host veth0 "$mtu"
|
||||
check_iface_mtu host podman0 "$mtu"
|
||||
}
|
||||
|
||||
|
||||
function add_default_route() {
|
||||
run_in_host_netns ip link add default_route type dummy
|
||||
run_in_host_netns ip link set default_route mtu 9000
|
||||
run_in_host_netns ip addr add 192.168.0.0/24 dev default_route
|
||||
run_in_host_netns ip link set default_route up
|
||||
run_in_host_netns ip route add default via 192.168.0.0
|
||||
}
|
||||
|
||||
function add_bridge() {
|
||||
run_in_host_netns ip link add podman0 type bridge
|
||||
run_in_host_netns ip link set podman0 mtu 9001
|
||||
run_in_host_netns ip link set up podman0
|
||||
}
|
||||
|
||||
@test "bridge - mtu from default route" {
|
||||
add_default_route
|
||||
run_netavark --file ${TESTSDIR}/testfiles/bridge-managed.json setup $(get_container_netns_path)
|
||||
check_mtu 9000
|
||||
}
|
||||
|
||||
@test "bridge - mtu from existing bridge" {
|
||||
add_bridge
|
||||
run_netavark --file ${TESTSDIR}/testfiles/bridge-managed.json setup $(get_container_netns_path)
|
||||
check_mtu 9001
|
||||
}
|
||||
|
||||
@test "bridge - mtu from config with default route" {
|
||||
add_default_route
|
||||
run_netavark --file ${TESTSDIR}/testfiles/bridge-mtu.json setup $(get_container_netns_path)
|
||||
check_mtu 9002
|
||||
}
|
||||
|
||||
@test "bridge - mtu from config with existing bridge" {
|
||||
add_bridge
|
||||
run_netavark --file ${TESTSDIR}/testfiles/bridge-mtu.json setup $(get_container_netns_path)
|
||||
check_iface_mtu container eth0 9002
|
||||
check_iface_mtu host veth0 9002
|
||||
# The existing bridge MTU should not be overriden.
|
||||
check_iface_mtu host podman0 9001
|
||||
}
|
|
@ -113,7 +113,10 @@ function teardown() {
|
|||
function create_netns() {
|
||||
# create a new netns and mountns and run a sleep process to keep it alive
|
||||
# we have to redirect stdout/err to /dev/null otherwise bats will hang
|
||||
unshare -nm sleep inf &>/dev/null &
|
||||
unshare -nm --propagation private sleep inf &>/dev/null &
|
||||
# netavark writes to /run/sysctl.d, mount a tmpfs to not leak stuff on the host
|
||||
mkdir -p /run/sysctl.d
|
||||
nsenter -n -m -w -t $! mount -t tmpfs none /run/sysctl.d
|
||||
echo $!
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,35 @@
|
|||
{
|
||||
"container_id": "6ce776ea58b5",
|
||||
"container_name": "testcontainer",
|
||||
"networks": {
|
||||
"podman1": {
|
||||
"static_ips": [
|
||||
"10.88.0.2"
|
||||
],
|
||||
"interface_name": "eth0"
|
||||
}
|
||||
},
|
||||
"network_info": {
|
||||
"podman1": {
|
||||
"name": "podman0",
|
||||
"id": "ed82e3a703682a9c09629d3cf45c1f1e7da5b32aeff3faf82837ef4d005356e6",
|
||||
"driver": "bridge",
|
||||
"network_interface": "podman0",
|
||||
"subnets": [
|
||||
{
|
||||
"gateway": "10.88.0.1",
|
||||
"subnet": "10.88.0.0/16"
|
||||
}
|
||||
],
|
||||
"ipv6_enabled": true,
|
||||
"internal": false,
|
||||
"dns_enabled": false,
|
||||
"ipam_options": {
|
||||
"driver": "host-local"
|
||||
},
|
||||
"options": {
|
||||
"mtu": "9002"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue