Compare commits

...

58 Commits

Author SHA1 Message Date
openshift-merge-bot[bot] 43127cf251
Merge pull request #1284 from containers/renovate/zbus-5.x
fix(deps): update rust crate zbus to 5.9.0
2025-07-17 10:16:02 +00:00
renovate[bot] 1614fafe78
fix(deps): update rust crate zbus to 5.9.0
Signed-off-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-07-16 20:59:30 +00:00
openshift-merge-bot[bot] 38535e4219
Merge pull request #1281 from jankaluza/23883-mtu
Set bridge MTU to match default route.
2025-07-16 14:57:06 +00:00
Jan Kaluza cd4d1ffb0b Set bridge MTU to match default route.
When creating a custom network, netavark previously used
the default Linux bridge MTU (1500), which undermined
performance optimization strategy of using a large MTU
(65520) for improved TCP throughput.

This patch ensures that when the user does not explicitly
set an MTU, podman attempts to derive a more suitable MTU
from the system's default route interface, preserving
intended performance benefits.

Also moved get_default_route_interface into core_utils for
reuse across modules.

Fixes: containers/podman#23883
Fixes: containers/podman#20009

Signed-off-by: Jan Kaluza <jkaluza@redhat.com>
2025-07-16 08:23:32 +02:00
openshift-merge-bot[bot] 98f1a4a301
Merge pull request #1278 from containers/renovate/hyper-util-0.x
fix(deps): update rust crate hyper-util to 0.1.15
2025-07-08 09:01:35 +00:00
openshift-merge-bot[bot] e29d9053b8
Merge pull request #1279 from containers/renovate/zbus-5.x
fix(deps): update rust crate zbus to 5.8.0
2025-07-08 08:45:00 +00:00
renovate[bot] 5c39692858
fix(deps): update rust crate zbus to 5.8.0
Signed-off-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-07-08 03:06:00 +00:00
renovate[bot] c40ee944fb
fix(deps): update rust crate hyper-util to 0.1.15
Signed-off-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-07-07 20:39:23 +00:00
openshift-merge-bot[bot] e9af8b0943
Merge pull request #1277 from containers/renovate/tokio-1.x
fix(deps): update rust crate tokio to 1.46.1
2025-07-07 12:26:58 +00:00
renovate[bot] 4c5833d4e6
fix(deps): update rust crate tokio to 1.46.1
Signed-off-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-07-04 21:50:54 +00:00
openshift-merge-bot[bot] b9ee44657e
Merge pull request #1276 from containers/renovate/tokio-1.x
fix(deps): update rust crate tokio to 1.46.0
2025-07-03 16:37:56 +00:00
renovate[bot] e2d1f6e2de
fix(deps): update rust crate tokio to 1.46.0
Signed-off-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-07-02 15:49:13 +00:00
openshift-merge-bot[bot] 24c1577be9
Merge pull request #1275 from Luap99/lint-1.88
fix new rust 1.88 lint errors
2025-07-01 14:46:22 +00:00
Paul Holzinger e5b23f0be8
fix new rust 1.88 lint errors
Just some slight formatting for the format!() macros.
I migrated them using cargo clippy --fix.

Also one complain about a empty line in a doc comment.

Signed-off-by: Paul Holzinger <pholzing@redhat.com>
2025-07-01 11:15:29 +02:00
openshift-merge-bot[bot] 9c4cd2ef8c
Merge pull request #1274 from containers/renovate/clap-4.x
fix(deps): update rust crate clap to ~4.5.40
2025-07-01 09:06:30 +00:00
renovate[bot] 083e525c49
fix(deps): update rust crate clap to ~4.5.40
Signed-off-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-07-01 04:05:22 +00:00
openshift-merge-bot[bot] d22d9d5dec
Merge pull request #1268 from Luap99/lint
fix new clippy lint warnings
2025-06-09 13:40:52 +00:00
openshift-merge-bot[bot] 5f260d6a86
Merge pull request #1267 from Luap99/nix
update nix to v0.30.1
2025-06-09 13:38:08 +00:00
openshift-merge-bot[bot] 4a7531e02f
Merge pull request #1270 from containers/renovate/hyper-util-0.x
fix(deps): update rust crate hyper-util to 0.1.14
2025-06-05 09:13:11 +00:00
renovate[bot] 7e93c15eb9
fix(deps): update rust crate hyper-util to 0.1.14
Signed-off-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-06-04 19:29:48 +00:00
Paul Holzinger 7105bee1de
make more use of NetavarkError
A clippy lint[1] wants us to use the other() function on the io error.
Howevr we already have our own error type for custom messages so just
use that and no co-opt the std::io::Error type.

[1] https://rust-lang.github.io/rust-clippy/master/index.html#io_other_error

Signed-off-by: Paul Holzinger <pholzing@redhat.com>
2025-06-03 15:16:50 +02:00
Paul Holzinger bc1eeda17f
drop DhcpProxy error type from NetavarkError
The tonic status is quite large and thus a new clippy lint now seem to
fail[1].

Only the test binary used the variant anyway so just drop it and also
print only the error text as the metadata is not useful in the error
message. Then update the tests to check the message instead of exit
code.

[1] https://rust-lang.github.io/rust-clippy/master/index.html#result_large_err

Signed-off-by: Paul Holzinger <pholzing@redhat.com>
2025-06-03 14:14:48 +02:00
Paul Holzinger 8188984938
update nix to v0.30.1
Signed-off-by: Paul Holzinger <pholzing@redhat.com>
2025-06-02 11:57:25 +02:00
openshift-merge-bot[bot] 4f36dbf37e
Merge pull request #1266 from containers/renovate/clap-4.x
fix(deps): update rust crate clap to ~4.5.39
2025-06-02 09:23:40 +00:00
openshift-merge-bot[bot] 838e39fe03
Merge pull request #1264 from containers/renovate/zbus-5.x
fix(deps): update rust crate zbus to 5.7.1
2025-06-02 09:18:40 +00:00
renovate[bot] 6a08cd64d9
fix(deps): update rust crate clap to ~4.5.39
Signed-off-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-06-01 01:20:50 +00:00
renovate[bot] 204dc49f57
fix(deps): update rust crate zbus to 5.7.1
Signed-off-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-05-30 22:09:05 +00:00
openshift-merge-bot[bot] a956db1f91
Merge pull request #1245 from Luap99/sysctl
sysctl rework
2025-05-30 22:07:37 +00:00
Paul Holzinger 060f55dc2c
write bridge sysctl to config file
When writing directly to /proc/sys we have the risk that the system
later overwrites the values again, for systemd distros systemd-sysctl
seems to manage that and reapplies sysctls based of the sysctl.d config
files.

Now as RHEL ships a config file that set rp_filter = 1 by default this
means that our rp_filter = 2 could be overwritten again by it.
So in short the original fix to set rp_filter = 2 never worked actually
as the value got overwritten again.

So to actually fix this create a sysctl.d config file for the bridge
with all its required sysctl settings. However we connot only do that,
as this happens async systemd might have no set the value by the time we
start the container process. So to avoid any reaces we do both.
Also of course we should only do that when systemd is in use and when
run as root, as rootless we operate in our own private netns so systemd
doesn't see the interfaces there and we do not have the privileges to
write to /run either.

Also always set ipv4/6 global forwarding and don't use a sync once,
when multiple networks are in use we do set them again but we now want
them to write them into the per bridge sysctl.d config file.

That way we can make these sysctls track for all running bridges and
once all contianer are stopped we do not end up with leaked sysctl.d
file which seems nice. And we also only have to handle one file at a
time.

Then move the internal forwarding disable right next to the global
enable and only enable it globally when not internal.
Lastly always set the route_localnet even when no ports are given.
The reason is we now set the sysctl only once when we create the bridge.
This offers the advantage that when starting many containers on the same
network we only have to configure the bridge specifc sysctl values once.

Fixes: 9a73a52 ("fix port forward with strict RPF and multi networks")
Fixes: https://issues.redhat.com/browse/RHEL-89477

Signed-off-by: Paul Holzinger <pholzing@redhat.com>
2025-05-30 18:25:13 +02:00
Paul Holzinger b67d91b083
extract systemd path check to core_utils
I need that in other place in the next commit so put it in the
core_utils module.

Signed-off-by: Paul Holzinger <pholzing@redhat.com>
2025-05-30 18:25:13 +02:00
Paul Holzinger 35f1db6bf2
move sysctl code into separate module
This should make the code easier to work with. I will add more
functionallity later.

Signed-off-by: Paul Holzinger <pholzing@redhat.com>
2025-05-30 18:25:12 +02:00
Paul Holzinger 1d5ee4e7b3
wrap sysctl name in error
It is much more helpful to print the actual sysctl name that we failed
to set.

Signed-off-by: Paul Holzinger <pholzing@redhat.com>
2025-05-30 18:25:12 +02:00
Paul Holzinger cdf7302422
drop sysctl dependency
We really don't need it, we can open/read/write the files directly which
is not more complex. The one thing we don't do is accept the path in any
format but I see this an benifit we should be consitent in how we pass
in the path name.

This also removed one extra open call on the file so this is better.

Signed-off-by: Paul Holzinger <pholzing@redhat.com>
2025-05-30 18:25:12 +02:00
Paul Holzinger f6604ebf62
update MSRV to v1.83
Allows us to use ErrorKind::ReadOnlyFilesystem, see the following commits.

Signed-off-by: Paul Holzinger <pholzing@redhat.com>
2025-05-30 18:25:12 +02:00
openshift-merge-bot[bot] 7be5e4d0af
Merge pull request #1263 from flouthoc/teardown-timer
dhcp_proxy: set `timeout_sender` only if required
2025-05-30 12:59:42 +00:00
flouthoc acd3949531
dhcp_proxy: set timeout_sender only if required
When `--activity-timeout 0` there should be no reason to push messages
on `activity_timeout_tx` as `activity_timeout_rx` is not receving any
messages as a result all the messages pushed are just stuck in queue,
resulting in `no available capacity` when queue becomes full.

Closes: https://github.com/containers/netavark/issues/1262

Signed-off-by: flouthoc <flouthoc.git@gmail.com>
2025-05-28 20:49:34 -07:00
openshift-merge-bot[bot] 9d8b95deb4
Merge pull request #1261 from flouthoc/bump-mozim
cargo: bump `mozim` to `0.2.6`
2025-05-28 15:50:23 +00:00
flouthoc 769a2ac528
cargo: bump mozim to 0.2.6
Contains patch for https://github.com/nispor/mozim/issues/49

Fixes: https://github.com/containers/netavark/issues/811
Closes: RUN-2720

Signed-off-by: flouthoc <flouthoc.git@gmail.com>
2025-05-28 08:05:11 -07:00
openshift-merge-bot[bot] b34f448790
Merge pull request #1259 from containers/renovate/hyper-util-0.x
fix(deps): update rust crate hyper-util to 0.1.13
2025-05-28 11:21:28 +00:00
renovate[bot] b6e12b2f39
fix(deps): update rust crate hyper-util to 0.1.13
Signed-off-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-05-27 20:26:59 +00:00
openshift-merge-bot[bot] 068abc869b
Merge pull request #1256 from Luap99/revert-search
Revert "remove search domain from response"
2025-05-27 14:02:34 +00:00
Paul Holzinger 03f12695a6
Revert "remove search domain from response"
This reverts commit 9035c67733.
The commit broke the name lookup for podman containers when other host
search domains are defined that respond to the same name.

Fixes: containers/podman#26198

Signed-off-by: Paul Holzinger <pholzing@redhat.com>
2025-05-27 15:01:41 +02:00
openshift-merge-bot[bot] 40515a58ef
Merge pull request #1255 from containers/renovate/tokio-1.x
fix(deps): update rust crate tokio to 1.45.1
2025-05-26 10:11:35 +00:00
renovate[bot] 0a6db8de33
fix(deps): update rust crate tokio to 1.45.1
Signed-off-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-05-24 15:20:41 +00:00
openshift-merge-bot[bot] 99621542d7
Merge pull request #1254 from Luap99/deps
minor dependency updates
2025-05-21 14:53:16 +00:00
Paul Holzinger fca86bbb6a
update netlink-packet-route to v0.23.0
Signed-off-by: Paul Holzinger <pholzing@redhat.com>
2025-05-21 13:50:35 +02:00
Paul Holzinger 3514007090
update nix to v0.30.1
Signed-off-by: Paul Holzinger <pholzing@redhat.com>
2025-05-21 13:48:01 +02:00
Paul Holzinger 23ba475ceb
remove unused nispor from Cargo.toml
It is still required via mozim so we do not actually get rid of it but
it is not a direct dependency so we should drop it so that renovate
doesn't bump it without reasons.

Signed-off-by: Paul Holzinger <pholzing@redhat.com>
2025-05-21 13:44:05 +02:00
openshift-merge-bot[bot] a528bf86e1
Merge pull request #1253 from containers/renovate/zbus-5.x
fix(deps): update rust crate zbus to 5.7.1
2025-05-21 08:41:23 +00:00
renovate[bot] ecb32b3b46
fix(deps): update rust crate zbus to 5.7.1
Signed-off-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-05-20 22:54:56 +00:00
openshift-merge-bot[bot] 8ba9c25b32
Merge pull request #1251 from containers/renovate/hyper-util-0.x
fix(deps): update rust crate hyper-util to 0.1.12
2025-05-20 10:55:45 +00:00
renovate[bot] c24391b6a0
fix(deps): update rust crate hyper-util to 0.1.12
Signed-off-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-05-19 20:50:40 +00:00
openshift-merge-bot[bot] c9649e875a
Merge pull request #1250 from Luap99/rpm-desc
rpm: update description
2025-05-13 19:35:24 +00:00
Paul Holzinger a1046af2b8
rpm: update description
We do support nftables for a while now. Just noticed while checking the
fedora builds.

Signed-off-by: Paul Holzinger <pholzing@redhat.com>
2025-05-13 18:27:34 +02:00
openshift-merge-bot[bot] 58ce8a49d9
Merge pull request #1248 from containers/renovate/zbus-5.x
fix(deps): update rust crate zbus to 5.7.0
2025-05-13 16:21:51 +00:00
openshift-merge-bot[bot] 6e3d2d04d1
Merge pull request #1249 from Luap99/release-1.15
Release v1.15.0
2025-05-13 15:20:28 +00:00
Paul Holzinger 8f6f84d098
bump to v1.16.0-dev
Signed-off-by: Paul Holzinger <pholzing@redhat.com>
2025-05-13 16:39:22 +02:00
renovate[bot] c3828405dc
fix(deps): update rust crate zbus to 5.7.0
Signed-off-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-05-13 14:31:35 +00:00
36 changed files with 797 additions and 604 deletions

View File

@ -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

154
Cargo.lock generated
View File

@ -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",

View File

@ -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"] }

View File

@ -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

View File

@ -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}");
}

View File

@ -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

View File

@ -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.

View File

@ -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
}
}

View File

@ -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);
}

View File

@ -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}"
))
})
}

View File

@ -152,7 +152,7 @@ impl Setup {
);
}
}
debug!("{:#?}", response);
debug!("{response:#?}");
let response_json = serde_json::to_string(&response)?;
println!("{response_json}");
debug!("Setup complete");

View File

@ -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(())
}
}

View File

@ -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)?;

View File

@ -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)
}

View File

@ -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.
///

View File

@ -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)
}

View File

@ -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,
}

View File

@ -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)

View File

@ -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
}
}

View File

@ -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())
))
}
};

View File

@ -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());
}
}

View File

@ -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}");
}

View File

@ -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),
));
}

View File

@ -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"),
}
}

View File

@ -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)
}

View File

@ -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 {

View File

@ -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!(

184
src/network/sysctl.rs Normal file
View File

@ -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()"
)
}
}

View File

@ -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"))
}

View File

@ -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"
}

View File

@ -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

View File

@ -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

View File

@ -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"

68
test/640-bridge-mtu.bats Normal file
View File

@ -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
}

View File

@ -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 $!
}

View File

@ -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"
}
}
}
}