ethtool-0.2.5/.cargo_vcs_info.json0000644000000001360000000000100125160ustar { "git": { "sha1": "96a97a737cb6250dec07766545ae04c7e456ddf3" }, "path_in_vcs": "" }ethtool-0.2.5/.github/workflows/clippy-rustfmt.yml000064400000000000000000000012701046102023000204300ustar 00000000000000name: Rustfmt and clippy check on: pull_request: types: [opened, synchronize, reopened] push: branches: - main jobs: rustfmt_clippy: strategy: fail-fast: true name: Rustfmt and clippy check runs-on: ubuntu-latest steps: - uses: actions/checkout@v3 - name: Install Rust Nightly run: | rustup override set nightly rustup update nightly rustup component add rustfmt clippy - name: rustfmt run: cargo fmt --all -- --check - name: clippy-tokio-socket run: cargo clippy - name: clippy-smol-socket run: cargo clippy --no-default-features --features smol_socket ethtool-0.2.5/.github/workflows/license.yml000064400000000000000000000005201046102023000170450ustar 00000000000000name: license on: pull_request: types: [opened, synchronize, reopened] push: branches: - main jobs: check-license: name: Check License runs-on: ubuntu-latest timeout-minutes: 3 steps: - uses: actions/checkout@v3 - name: Check License Header uses: apache/skywalking-eyes@v0.3.0 ethtool-0.2.5/.github/workflows/main.yml000064400000000000000000000011021046102023000163440ustar 00000000000000name: CI on: pull_request: types: [opened, synchronize, reopened] push: branches: - main jobs: ci: name: CI (stable) runs-on: ubuntu-latest steps: - uses: actions/checkout@v3 - name: Install Rust Stable run: | rustup override set stable rustup update stable - name: Test with default feature run: cargo test - name: Test with tokio feature run: cargo test --features tokio_socket - name: Test with smol_socket feature run: cargo test --features smol_socket ethtool-0.2.5/.gitignore000064400000000000000000000000411046102023000132710ustar 00000000000000Cargo.lock target vendor/ *.swp ethtool-0.2.5/.licenserc.yaml000064400000000000000000000003711046102023000142200ustar 00000000000000header: license: content: | SPDX-License-Identifier: MIT paths-ignore: - 'target' - '**/*.toml' - '**/*.lock' - '**/*.yml' - '**/*.md' - 'CHANGELOG' - 'LICENSE-MIT' - '.gitignore' comment: on-failure ethtool-0.2.5/.rustfmt.toml000064400000000000000000000001151046102023000137620ustar 00000000000000 max_width = 80 wrap_comments = true reorder_imports = true edition = "2021" ethtool-0.2.5/CHANGELOG000064400000000000000000000005321046102023000125200ustar 00000000000000# Changelog ## [0.2.5] - 2023-07-10 ### Breaking changes - N/A ### New features - Support time stamp (`ethtool --show-time-stamping`). (b753d74) ### Bug fixes - Use latest rust-netlink crates. (615b168) ## [0.2.4] - 2023-01-29 ### Breaking changes - N/A ### New features - N/A ### Bug fixes - Use latest rust-netlink crates. (151f217) ethtool-0.2.5/Cargo.lock0000644000000630740000000000100105030ustar # This file is automatically @generated by Cargo. # It is not intended for manual editing. version = 3 [[package]] name = "addr2line" version = "0.20.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f4fa78e18c64fce05e902adecd7a5eed15a5e0a3439f7b0e169f0252214865e3" dependencies = [ "gimli", ] [[package]] name = "adler" version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" [[package]] name = "aho-corasick" version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "43f6cb1bf222025340178f382c426f13757b2960e89779dfcb319c32542a5a41" dependencies = [ "memchr", ] [[package]] name = "anyhow" version = "1.0.71" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9c7d0618f0e0b7e8ff11427422b64564d5fb0be1940354bfe2e0529b18a9d9b8" [[package]] name = "async-channel" version = "1.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "81953c529336010edd6d8e358f886d9581267795c61b19475b71314bffa46d35" dependencies = [ "concurrent-queue", "event-listener", "futures-core", ] [[package]] name = "async-executor" version = "1.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6fa3dc5f2a8564f07759c008b9109dc0d39de92a88d5588b8a5036d286383afb" dependencies = [ "async-lock", "async-task", "concurrent-queue", "fastrand", "futures-lite", "slab", ] [[package]] name = "async-global-executor" version = "2.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f1b6f5d7df27bd294849f8eec66ecfc63d11814df7a4f5d74168a2394467b776" dependencies = [ "async-channel", "async-executor", "async-io", "async-lock", "blocking", "futures-lite", "once_cell", ] [[package]] name = "async-io" version = "1.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0fc5b45d93ef0529756f812ca52e44c221b35341892d3dcc34132ac02f3dd2af" dependencies = [ "async-lock", "autocfg", "cfg-if", "concurrent-queue", "futures-lite", "log", "parking", "polling", "rustix", "slab", "socket2", "waker-fn", ] [[package]] name = "async-lock" version = "2.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fa24f727524730b077666307f2734b4a1a1c57acb79193127dcc8914d5242dd7" dependencies = [ "event-listener", ] [[package]] name = "async-std" version = "1.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "62565bb4402e926b29953c785397c6dc0391b7b446e45008b0049eb43cec6f5d" dependencies = [ "async-channel", "async-global-executor", "async-io", "async-lock", "crossbeam-utils", "futures-channel", "futures-core", "futures-io", "futures-lite", "gloo-timers", "kv-log-macro", "log", "memchr", "once_cell", "pin-project-lite", "pin-utils", "slab", "wasm-bindgen-futures", ] [[package]] name = "async-task" version = "4.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ecc7ab41815b3c653ccd2978ec3255c81349336702dfdf62ee6f7069b12a3aae" [[package]] name = "atomic-waker" version = "1.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1181e1e0d1fce796a03db1ae795d67167da795f9cf4a39c37589e85ef57f26d3" [[package]] name = "atty" version = "0.2.14" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8" dependencies = [ "hermit-abi 0.1.19", "libc", "winapi", ] [[package]] name = "autocfg" version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" [[package]] name = "backtrace" version = "0.3.68" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4319208da049c43661739c5fade2ba182f09d1dc2299b32298d3a31692b17e12" dependencies = [ "addr2line", "cc", "cfg-if", "libc", "miniz_oxide", "object", "rustc-demangle", ] [[package]] name = "bitflags" version = "1.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" [[package]] name = "blocking" version = "1.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "77231a1c8f801696fc0123ec6150ce92cffb8e164a02afb9c8ddee0e9b65ad65" dependencies = [ "async-channel", "async-lock", "async-task", "atomic-waker", "fastrand", "futures-lite", "log", ] [[package]] name = "bumpalo" version = "3.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a3e2c3daef883ecc1b5d58c15adae93470a91d425f3532ba1695849656af3fc1" [[package]] name = "byteorder" version = "1.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610" [[package]] name = "bytes" version = "1.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "89b2fd2a0dcf38d7971e2194b6b6eebab45ae01067456a7fd93d5547a61b70be" [[package]] name = "cc" version = "1.0.79" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "50d30906286121d95be3d479533b458f87493b30a4b5f79a607db8f5d11aa91f" [[package]] name = "cfg-if" version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" [[package]] name = "concurrent-queue" version = "2.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "62ec6771ecfa0762d24683ee5a32ad78487a3d3afdc0fb8cae19d2c5deb50b7c" dependencies = [ "crossbeam-utils", ] [[package]] name = "crossbeam-utils" version = "0.8.16" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5a22b2d63d4d1dc0b7f1b6b2747dd0088008a9be28b6ddf0b1e7d335e3037294" dependencies = [ "cfg-if", ] [[package]] name = "env_logger" version = "0.9.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a12e6657c4c97ebab115a42dcee77225f7f482cdd841cf7088c657a42e9e00e7" dependencies = [ "atty", "humantime", "log", "regex", "termcolor", ] [[package]] name = "errno" version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4bcfec3a70f97c962c307b2d2c56e358cf1d00b558d74262b5f929ee8cc7e73a" dependencies = [ "errno-dragonfly", "libc", "windows-sys", ] [[package]] name = "errno-dragonfly" version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "aa68f1b12764fab894d2755d2518754e71b4fd80ecfb822714a1206c2aab39bf" dependencies = [ "cc", "libc", ] [[package]] name = "ethtool" version = "0.2.5" dependencies = [ "anyhow", "async-std", "byteorder", "env_logger", "futures", "genetlink", "log", "netlink-packet-core", "netlink-packet-generic", "netlink-packet-utils", "netlink-proto", "netlink-sys", "thiserror", "tokio", ] [[package]] name = "event-listener" version = "2.5.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0206175f82b8d6bf6652ff7d71a1e27fd2e4efde587fd368662814d6ec1d9ce0" [[package]] name = "fastrand" version = "1.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e51093e27b0797c359783294ca4f0a911c270184cb10f85783b118614a1501be" dependencies = [ "instant", ] [[package]] name = "futures" version = "0.3.28" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "23342abe12aba583913b2e62f22225ff9c950774065e4bfb61a19cd9770fec40" dependencies = [ "futures-channel", "futures-core", "futures-executor", "futures-io", "futures-sink", "futures-task", "futures-util", ] [[package]] name = "futures-channel" version = "0.3.28" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "955518d47e09b25bbebc7a18df10b81f0c766eaf4c4f1cccef2fca5f2a4fb5f2" dependencies = [ "futures-core", "futures-sink", ] [[package]] name = "futures-core" version = "0.3.28" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4bca583b7e26f571124fe5b7561d49cb2868d79116cfa0eefce955557c6fee8c" [[package]] name = "futures-executor" version = "0.3.28" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ccecee823288125bd88b4d7f565c9e58e41858e47ab72e8ea2d64e93624386e0" dependencies = [ "futures-core", "futures-task", "futures-util", ] [[package]] name = "futures-io" version = "0.3.28" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4fff74096e71ed47f8e023204cfd0aa1289cd54ae5430a9523be060cdb849964" [[package]] name = "futures-lite" version = "1.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "49a9d51ce47660b1e808d3c990b4709f2f415d928835a17dfd16991515c46bce" dependencies = [ "fastrand", "futures-core", "futures-io", "memchr", "parking", "pin-project-lite", "waker-fn", ] [[package]] name = "futures-macro" version = "0.3.28" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "89ca545a94061b6365f2c7355b4b32bd20df3ff95f02da9329b34ccc3bd6ee72" dependencies = [ "proc-macro2", "quote", "syn", ] [[package]] name = "futures-sink" version = "0.3.28" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f43be4fe21a13b9781a69afa4985b0f6ee0e1afab2c6f454a8cf30e2b2237b6e" [[package]] name = "futures-task" version = "0.3.28" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "76d3d132be6c0e6aa1534069c705a74a5997a356c0dc2f86a47765e5617c5b65" [[package]] name = "futures-util" version = "0.3.28" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "26b01e40b772d54cf6c6d721c1d1abd0647a0106a12ecaa1c186273392a69533" dependencies = [ "futures-channel", "futures-core", "futures-io", "futures-macro", "futures-sink", "futures-task", "memchr", "pin-project-lite", "pin-utils", "slab", ] [[package]] name = "genetlink" version = "0.2.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1f890076c1faa1298bf747ce3694a8d9e0d2cc4b06fe293f12dd95742bfd079f" dependencies = [ "futures", "log", "netlink-packet-core", "netlink-packet-generic", "netlink-packet-utils", "netlink-proto", "thiserror", ] [[package]] name = "gimli" version = "0.27.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b6c80984affa11d98d1b88b66ac8853f143217b399d3c74116778ff8fdb4ed2e" [[package]] name = "gloo-timers" version = "0.2.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9b995a66bb87bebce9a0f4a95aed01daca4872c050bfcb21653361c03bc35e5c" dependencies = [ "futures-channel", "futures-core", "js-sys", "wasm-bindgen", ] [[package]] name = "hermit-abi" version = "0.1.19" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "62b467343b94ba476dcb2500d242dadbb39557df889310ac77c5d99100aaac33" dependencies = [ "libc", ] [[package]] name = "hermit-abi" version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "443144c8cdadd93ebf52ddb4056d257f5b52c04d3c804e657d19eb73fc33668b" [[package]] name = "humantime" version = "2.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4" [[package]] name = "instant" version = "0.1.12" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7a5bbe824c507c5da5956355e86a746d82e0e1464f65d862cc5e71da70e94b2c" dependencies = [ "cfg-if", ] [[package]] name = "io-lifetimes" version = "1.0.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "eae7b9aee968036d54dce06cebaefd919e4472e753296daccd6d344e3e2df0c2" dependencies = [ "hermit-abi 0.3.2", "libc", "windows-sys", ] [[package]] name = "js-sys" version = "0.3.64" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c5f195fe497f702db0f318b07fdd68edb16955aed830df8363d837542f8f935a" dependencies = [ "wasm-bindgen", ] [[package]] name = "kv-log-macro" version = "1.0.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0de8b303297635ad57c9f5059fd9cee7a47f8e8daa09df0fcd07dd39fb22977f" dependencies = [ "log", ] [[package]] name = "libc" version = "0.2.147" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b4668fb0ea861c1df094127ac5f1da3409a82116a4ba74fca2e58ef927159bb3" [[package]] name = "linux-raw-sys" version = "0.3.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ef53942eb7bf7ff43a617b3e2c1c4a5ecf5944a7c1bc12d7ee39bbb15e5c1519" [[package]] name = "log" version = "0.4.19" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b06a4cde4c0f271a446782e3eff8de789548ce57dbc8eca9292c27f4a42004b4" dependencies = [ "value-bag", ] [[package]] name = "memchr" version = "2.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d" [[package]] name = "miniz_oxide" version = "0.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e7810e0be55b428ada41041c41f32c9f1a42817901b4ccf45fa3d4b6561e74c7" dependencies = [ "adler", ] [[package]] name = "mio" version = "0.8.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "927a765cd3fc26206e66b296465fa9d3e5ab003e651c1b3c060e7956d96b19d2" dependencies = [ "libc", "wasi", "windows-sys", ] [[package]] name = "netlink-packet-core" version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "72724faf704479d67b388da142b186f916188505e7e0b26719019c525882eda4" dependencies = [ "anyhow", "byteorder", "netlink-packet-utils", ] [[package]] name = "netlink-packet-generic" version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1cd7eb8ad331c84c6b8cb7f685b448133e5ad82e1ffd5acafac374af4a5a308b" dependencies = [ "anyhow", "byteorder", "netlink-packet-core", "netlink-packet-utils", ] [[package]] name = "netlink-packet-utils" version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0ede8a08c71ad5a95cdd0e4e52facd37190977039a4704eb82a283f713747d34" dependencies = [ "anyhow", "byteorder", "paste", "thiserror", ] [[package]] name = "netlink-proto" version = "0.11.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "842c6770fc4bb33dd902f41829c61ef872b8e38de1405aa0b938b27b8fba12c3" dependencies = [ "bytes", "futures", "log", "netlink-packet-core", "netlink-sys", "thiserror", "tokio", ] [[package]] name = "netlink-sys" version = "0.8.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6471bf08e7ac0135876a9581bf3217ef0333c191c128d34878079f42ee150411" dependencies = [ "async-io", "bytes", "futures", "libc", "log", "tokio", ] [[package]] name = "num_cpus" version = "1.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4161fcb6d602d4d2081af7c3a45852d875a03dd337a6bfdd6e06407b61342a43" dependencies = [ "hermit-abi 0.3.2", "libc", ] [[package]] name = "object" version = "0.31.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8bda667d9f2b5051b8833f59f3bf748b28ef54f850f4fcb389a252aa383866d1" dependencies = [ "memchr", ] [[package]] name = "once_cell" version = "1.18.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dd8b5dd2ae5ed71462c540258bedcb51965123ad7e7ccf4b9a8cafaa4a63576d" [[package]] name = "parking" version = "2.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "14f2252c834a40ed9bb5422029649578e63aa341ac401f74e719dd1afda8394e" [[package]] name = "paste" version = "1.0.13" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b4b27ab7be369122c218afc2079489cdcb4b517c0a3fc386ff11e1fedfcc2b35" [[package]] name = "pin-project-lite" version = "0.2.10" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4c40d25201921e5ff0c862a505c6557ea88568a4e3ace775ab55e93f2f4f9d57" [[package]] name = "pin-utils" version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" [[package]] name = "polling" version = "2.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4b2d323e8ca7996b3e23126511a523f7e62924d93ecd5ae73b333815b0eb3dce" dependencies = [ "autocfg", "bitflags", "cfg-if", "concurrent-queue", "libc", "log", "pin-project-lite", "windows-sys", ] [[package]] name = "proc-macro2" version = "1.0.64" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "78803b62cbf1f46fde80d7c0e803111524b9877184cfe7c3033659490ac7a7da" dependencies = [ "unicode-ident", ] [[package]] name = "quote" version = "1.0.29" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "573015e8ab27661678357f27dc26460738fd2b6c86e46f386fde94cb5d913105" dependencies = [ "proc-macro2", ] [[package]] name = "regex" version = "1.9.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b2eae68fc220f7cf2532e4494aded17545fce192d59cd996e0fe7887f4ceb575" dependencies = [ "aho-corasick", "memchr", "regex-automata", "regex-syntax", ] [[package]] name = "regex-automata" version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "83d3daa6976cffb758ec878f108ba0e062a45b2d6ca3a2cca965338855476caf" dependencies = [ "aho-corasick", "memchr", "regex-syntax", ] [[package]] name = "regex-syntax" version = "0.7.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2ab07dc67230e4a4718e70fd5c20055a4334b121f1f9db8fe63ef39ce9b8c846" [[package]] name = "rustc-demangle" version = "0.1.23" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d626bb9dae77e28219937af045c257c28bfd3f69333c512553507f5f9798cb76" [[package]] name = "rustix" version = "0.37.23" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4d69718bf81c6127a49dc64e44a742e8bb9213c0ff8869a22c308f84c1d4ab06" dependencies = [ "bitflags", "errno", "io-lifetimes", "libc", "linux-raw-sys", "windows-sys", ] [[package]] name = "slab" version = "0.4.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6528351c9bc8ab22353f9d776db39a20288e8d6c37ef8cfe3317cf875eecfc2d" dependencies = [ "autocfg", ] [[package]] name = "socket2" version = "0.4.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "64a4a911eed85daf18834cfaa86a79b7d266ff93ff5ba14005426219480ed662" dependencies = [ "libc", "winapi", ] [[package]] name = "syn" version = "2.0.25" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "15e3fc8c0c74267e2df136e5e5fb656a464158aa57624053375eb9c8c6e25ae2" dependencies = [ "proc-macro2", "quote", "unicode-ident", ] [[package]] name = "termcolor" version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "be55cf8942feac5c765c2c993422806843c9a9a45d4d5c407ad6dd2ea95eb9b6" dependencies = [ "winapi-util", ] [[package]] name = "thiserror" version = "1.0.43" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a35fc5b8971143ca348fa6df4f024d4d55264f3468c71ad1c2f365b0a4d58c42" dependencies = [ "thiserror-impl", ] [[package]] name = "thiserror-impl" version = "1.0.43" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "463fe12d7993d3b327787537ce8dd4dfa058de32fc2b195ef3cde03dc4771e8f" dependencies = [ "proc-macro2", "quote", "syn", ] [[package]] name = "tokio" version = "1.29.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "532826ff75199d5833b9d2c5fe410f29235e25704ee5f0ef599fb51c21f4a4da" dependencies = [ "autocfg", "backtrace", "bytes", "libc", "mio", "num_cpus", "pin-project-lite", "socket2", "tokio-macros", "windows-sys", ] [[package]] name = "tokio-macros" version = "2.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "630bdcf245f78637c13ec01ffae6187cca34625e8c63150d424b59e55af2675e" dependencies = [ "proc-macro2", "quote", "syn", ] [[package]] name = "unicode-ident" version = "1.0.10" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "22049a19f4a68748a168c0fc439f9516686aa045927ff767eca0a85101fb6e73" [[package]] name = "value-bag" version = "1.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d92ccd67fb88503048c01b59152a04effd0782d035a83a6d256ce6085f08f4a3" [[package]] name = "waker-fn" version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9d5b2c62b4012a3e1eca5a7e077d13b3bf498c4073e33ccd58626607748ceeca" [[package]] name = "wasi" version = "0.11.0+wasi-snapshot-preview1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" [[package]] name = "wasm-bindgen" version = "0.2.87" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7706a72ab36d8cb1f80ffbf0e071533974a60d0a308d01a5d0375bf60499a342" dependencies = [ "cfg-if", "wasm-bindgen-macro", ] [[package]] name = "wasm-bindgen-backend" version = "0.2.87" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5ef2b6d3c510e9625e5fe6f509ab07d66a760f0885d858736483c32ed7809abd" dependencies = [ "bumpalo", "log", "once_cell", "proc-macro2", "quote", "syn", "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-futures" version = "0.4.37" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c02dbc21516f9f1f04f187958890d7e6026df8d16540b7ad9492bc34a67cea03" dependencies = [ "cfg-if", "js-sys", "wasm-bindgen", "web-sys", ] [[package]] name = "wasm-bindgen-macro" version = "0.2.87" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dee495e55982a3bd48105a7b947fd2a9b4a8ae3010041b9e0faab3f9cd028f1d" dependencies = [ "quote", "wasm-bindgen-macro-support", ] [[package]] name = "wasm-bindgen-macro-support" version = "0.2.87" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "54681b18a46765f095758388f2d0cf16eb8d4169b639ab575a8f5693af210c7b" dependencies = [ "proc-macro2", "quote", "syn", "wasm-bindgen-backend", "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-shared" version = "0.2.87" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ca6ad05a4870b2bf5fe995117d3728437bd27d7cd5f06f13c17443ef369775a1" [[package]] name = "web-sys" version = "0.3.64" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9b85cbef8c220a6abc02aefd892dfc0fc23afb1c6a426316ec33253a3877249b" dependencies = [ "js-sys", "wasm-bindgen", ] [[package]] name = "winapi" version = "0.3.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" dependencies = [ "winapi-i686-pc-windows-gnu", "winapi-x86_64-pc-windows-gnu", ] [[package]] name = "winapi-i686-pc-windows-gnu" version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" [[package]] name = "winapi-util" version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "70ec6ce85bb158151cae5e5c87f95a8e97d2c0c4b001223f33a334e3ce5de178" dependencies = [ "winapi", ] [[package]] name = "winapi-x86_64-pc-windows-gnu" version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" [[package]] name = "windows-sys" version = "0.48.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9" dependencies = [ "windows-targets", ] [[package]] name = "windows-targets" version = "0.48.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "05d4b17490f70499f20b9e791dcf6a299785ce8af4d709018206dc5b4953e95f" dependencies = [ "windows_aarch64_gnullvm", "windows_aarch64_msvc", "windows_i686_gnu", "windows_i686_msvc", "windows_x86_64_gnu", "windows_x86_64_gnullvm", "windows_x86_64_msvc", ] [[package]] name = "windows_aarch64_gnullvm" version = "0.48.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "91ae572e1b79dba883e0d315474df7305d12f569b400fcf90581b06062f7e1bc" [[package]] name = "windows_aarch64_msvc" version = "0.48.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b2ef27e0d7bdfcfc7b868b317c1d32c641a6fe4629c171b8928c7b08d98d7cf3" [[package]] name = "windows_i686_gnu" version = "0.48.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "622a1962a7db830d6fd0a69683c80a18fda201879f0f447f065a3b7467daa241" [[package]] name = "windows_i686_msvc" version = "0.48.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4542c6e364ce21bf45d69fdd2a8e455fa38d316158cfd43b3ac1c5b1b19f8e00" [[package]] name = "windows_x86_64_gnu" version = "0.48.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ca2b8a661f7628cbd23440e50b05d705db3686f894fc9580820623656af974b1" [[package]] name = "windows_x86_64_gnullvm" version = "0.48.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7896dbc1f41e08872e9d5e8f8baa8fdd2677f29468c4e156210174edc7f7b953" [[package]] name = "windows_x86_64_msvc" version = "0.48.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1a515f5799fe4961cb532f983ce2b23082366b898e52ffbce459c86f67c8378a" ethtool-0.2.5/Cargo.toml0000644000000035540000000000100105230ustar # THIS FILE IS AUTOMATICALLY GENERATED BY CARGO # # When uploading crates to the registry Cargo will automatically # "normalize" Cargo.toml files for maximal compatibility # with all versions of Cargo and also rewrite `path` dependencies # to registry (e.g., crates.io) dependencies. # # If you are reading this file be aware that the original Cargo.toml # will likely look very different (and much more reasonable). # See Cargo.toml.orig for the original contents. [package] edition = "2018" name = "ethtool" version = "0.2.5" authors = ["Gris Ge "] description = "Linux Ethtool Communication Library" readme = "README.md" keywords = ["network"] categories = [ "network-programming", "os", ] license = "MIT" [lib] name = "ethtool" crate-type = ["lib"] path = "src/lib.rs" [[example]] name = "dump_pause" required-features = ["tokio_socket"] [dependencies.anyhow] version = "1.0.44" [dependencies.async-std] version = "1.9.0" optional = true [dependencies.byteorder] version = "1.4.3" [dependencies.futures] version = "0.3.17" [dependencies.genetlink] version = "0.2.4" default-features = false [dependencies.log] version = "0.4.14" [dependencies.netlink-packet-core] version = "0.7.0" [dependencies.netlink-packet-generic] version = "0.3.3" [dependencies.netlink-packet-utils] version = "0.5.2" [dependencies.netlink-proto] version = "0.11.2" default-features = false [dependencies.netlink-sys] version = "0.8.4" [dependencies.thiserror] version = "1.0.29" [dependencies.tokio] version = "1.0.1" features = ["rt"] optional = true [dev-dependencies.env_logger] version = "0.9.0" [dev-dependencies.tokio] version = "1.11.0" features = [ "macros", "rt", "rt-multi-thread", ] [features] default = ["tokio_socket"] smol_socket = [ "netlink-proto/smol_socket", "async-std", ] tokio_socket = [ "netlink-proto/tokio_socket", "tokio", ] ethtool-0.2.5/Cargo.toml.orig000064400000000000000000000022351046102023000141770ustar 00000000000000[package] name = "ethtool" version = "0.2.5" authors = ["Gris Ge "] license = "MIT" edition = "2018" description = "Linux Ethtool Communication Library" keywords = ["network"] categories = ["network-programming", "os"] readme = "README.md" [lib] name = "ethtool" path = "src/lib.rs" crate-type = ["lib"] [features] default = ["tokio_socket"] tokio_socket = ["netlink-proto/tokio_socket", "tokio"] smol_socket = ["netlink-proto/smol_socket", "async-std"] [dependencies] anyhow = "1.0.44" async-std = { version = "1.9.0", optional = true} byteorder = "1.4.3" futures = "0.3.17" log = "0.4.14" genetlink = { default-features = false, version = "0.2.4"} netlink-packet-core = { version = "0.7.0"} netlink-packet-generic = { version = "0.3.3" } netlink-packet-utils = { version = "0.5.2" } netlink-proto = { default-features = false, version = "0.11.2" } netlink-sys = { version = "0.8.4" } thiserror = "1.0.29" tokio = { version = "1.0.1", features = ["rt"], optional = true} [dev-dependencies] tokio = { version = "1.11.0", features = ["macros", "rt", "rt-multi-thread"] } env_logger = "0.9.0" [[example]] name = "dump_pause" required-features = ["tokio_socket"] ethtool-0.2.5/LICENSE-MIT000064400000000000000000000027731046102023000127530ustar 00000000000000Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. Distributions of all or part of the Software intended to be used by the recipients as they would use the unmodified Software, containing modifications that substantially alter, remove, or disable functionality of the Software, outside of the documented configuration mechanisms provided by the Software, shall be modified such that the Original Author's bug reporting email addresses and urls are either replaced with the contact information of the parties responsible for the changes, or removed entirely. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ethtool-0.2.5/README.md000064400000000000000000000000371046102023000125650ustar 00000000000000# Rust crate for linux ethtool ethtool-0.2.5/examples/dump_coalesce.rs000064400000000000000000000014251046102023000162770ustar 00000000000000// SPDX-License-Identifier: MIT use futures::stream::TryStreamExt; // Once we find a way to load netsimdev kernel module in CI, we can convert this // to a test fn main() { let rt = tokio::runtime::Builder::new_current_thread() .enable_io() .build() .unwrap(); rt.block_on(get_coalesce(None)); } async fn get_coalesce(iface_name: Option<&str>) { let (connection, mut handle, _) = ethtool::new_connection().unwrap(); tokio::spawn(connection); let mut coalesce_handle = handle.coalesce().get(iface_name).execute().await; let mut msgs = Vec::new(); while let Some(msg) = coalesce_handle.try_next().await.unwrap() { msgs.push(msg); } assert!(!msgs.is_empty()); for msg in msgs { println!("{msg:?}"); } } ethtool-0.2.5/examples/dump_features.rs000064400000000000000000000014201046102023000163320ustar 00000000000000// SPDX-License-Identifier: MIT use futures::stream::TryStreamExt; // Once we find a way to load netsimdev kernel module in CI, we can convert this // to a test fn main() { let rt = tokio::runtime::Builder::new_current_thread() .enable_io() .build() .unwrap(); rt.block_on(get_feature(None)); } async fn get_feature(iface_name: Option<&str>) { let (connection, mut handle, _) = ethtool::new_connection().unwrap(); tokio::spawn(connection); let mut feature_handle = handle.feature().get(iface_name).execute().await; let mut msgs = Vec::new(); while let Some(msg) = feature_handle.try_next().await.unwrap() { msgs.push(msg); } assert!(!msgs.is_empty()); for msg in msgs { println!("{msg:?}"); } } ethtool-0.2.5/examples/dump_link_mode.rs000064400000000000000000000014421046102023000164610ustar 00000000000000// SPDX-License-Identifier: MIT use futures::stream::TryStreamExt; // Once we find a way to load netsimdev kernel module in CI, we can convert this // to a test fn main() { let rt = tokio::runtime::Builder::new_current_thread() .enable_io() .build() .unwrap(); rt.block_on(get_link_mode(None)); } async fn get_link_mode(iface_name: Option<&str>) { let (connection, mut handle, _) = ethtool::new_connection().unwrap(); tokio::spawn(connection); let mut link_mode_handle = handle.link_mode().get(iface_name).execute().await; let mut msgs = Vec::new(); while let Some(msg) = link_mode_handle.try_next().await.unwrap() { msgs.push(msg); } assert!(!msgs.is_empty()); for msg in msgs { println!("{msg:?}"); } } ethtool-0.2.5/examples/dump_pause.rs000064400000000000000000000013771046102023000156440ustar 00000000000000// SPDX-License-Identifier: MIT use futures::stream::TryStreamExt; // Once we find a way to load netsimdev kernel module in CI, we can convert this // to a test fn main() { env_logger::init(); let rt = tokio::runtime::Builder::new_current_thread() .enable_io() .build() .unwrap(); rt.block_on(get_pause(None)); } async fn get_pause(iface_name: Option<&str>) { let (connection, mut handle, _) = ethtool::new_connection().unwrap(); tokio::spawn(connection); let mut pause_handle = handle.pause().get(iface_name).execute().await; let mut msgs = Vec::new(); while let Some(msg) = pause_handle.try_next().await.unwrap() { msgs.push(msg); } assert!(!msgs.is_empty()); println!("{msgs:?}"); } ethtool-0.2.5/examples/dump_rings.rs000064400000000000000000000014011046102023000156350ustar 00000000000000// SPDX-License-Identifier: MIT use futures::stream::TryStreamExt; // Once we find a way to load netsimdev kernel module in CI, we can convert this // to a test fn main() { let rt = tokio::runtime::Builder::new_current_thread() .enable_io() .build() .unwrap(); rt.block_on(get_ring(None)); } async fn get_ring(iface_name: Option<&str>) { let (connection, mut handle, _) = ethtool::new_connection().unwrap(); tokio::spawn(connection); let mut ring_handle = handle.ring().get(iface_name).execute().await; let mut msgs = Vec::new(); while let Some(msg) = ring_handle.try_next().await.unwrap() { msgs.push(msg); } assert!(!msgs.is_empty()); for msg in msgs { println!("{msg:?}"); } } ethtool-0.2.5/examples/dump_tsinfo.rs000064400000000000000000000015141046102023000160220ustar 00000000000000// SPDX-License-Identifier: MIT use futures::stream::TryStreamExt; // Once we find a way to load netsimdev kernel module in CI, we can convert this // to a test fn main() { let rt = tokio::runtime::Builder::new_current_thread() .enable_io() .build() .unwrap(); let iface_name = std::env::args().nth(1); rt.block_on(get_tsinfo(iface_name.as_deref())); } async fn get_tsinfo(iface_name: Option<&str>) { let (connection, mut handle, _) = ethtool::new_connection().unwrap(); tokio::spawn(connection); let mut tsinfo_handle = handle.tsinfo().get(iface_name).execute().await; let mut msgs = Vec::new(); while let Some(msg) = tsinfo_handle.try_next().await.unwrap() { msgs.push(msg); } assert!(!msgs.is_empty()); for msg in msgs { println!("{:?}", msg); } } ethtool-0.2.5/src/bitset_util.rs000064400000000000000000000047001046102023000147730ustar 00000000000000// SPDX-License-Identifier: MIT use anyhow::Context; use log::warn; use netlink_packet_utils::{ nla::NlasIterator, parsers::parse_string, DecodeError, }; const ETHTOOL_A_BITSET_BITS: u16 = 3; const ETHTOOL_A_BITSET_BITS_BIT: u16 = 1; const ETHTOOL_A_BITSET_BIT_INDEX: u16 = 1; const ETHTOOL_A_BITSET_BIT_NAME: u16 = 2; const ETHTOOL_A_BITSET_BIT_VALUE: u16 = 3; pub(crate) fn parse_bitset_bits_nlas( raw: &[u8], ) -> Result, DecodeError> { let error_msg = "failed to parse mode bit sets"; for nla in NlasIterator::new(raw) { let nla = &nla.context(error_msg)?; if nla.kind() == ETHTOOL_A_BITSET_BITS { return parse_bitset_bits_nla(nla.value()); } } Err("No ETHTOOL_A_BITSET_BITS NLA found".into()) } fn parse_bitset_bits_nla(raw: &[u8]) -> Result, DecodeError> { let mut modes = Vec::new(); let error_msg = "Failed to parse ETHTOOL_A_BITSET_BITS attributes"; for bit_nla in NlasIterator::new(raw) { let bit_nla = &bit_nla.context(error_msg)?; match bit_nla.kind() { ETHTOOL_A_BITSET_BITS_BIT => { let error_msg = "Failed to parse ETHTOOL_A_BITSET_BITS_BIT attributes"; let nlas = NlasIterator::new(bit_nla.value()); for nla in nlas { let nla = &nla.context(error_msg)?; let payload = nla.value(); match nla.kind() { ETHTOOL_A_BITSET_BIT_INDEX | ETHTOOL_A_BITSET_BIT_VALUE => { // ignored } ETHTOOL_A_BITSET_BIT_NAME => { modes.push(parse_string(payload).context( "Invald ETHTOOL_A_BITSET_BIT_NAME value", )?); } _ => { warn!( "Unknown ETHTOOL_A_BITSET_BITS_BIT {} {:?}", nla.kind(), nla.value(), ); } } } } _ => { warn!( "Unknown ETHTOOL_A_BITSET_BITS kind {}, {:?}", bit_nla.kind(), bit_nla.value() ); } }; } Ok(modes) } ethtool-0.2.5/src/coalesce/attr.rs000064400000000000000000000275161046102023000152060ustar 00000000000000// SPDX-License-Identifier: MIT use anyhow::Context; use byteorder::{ByteOrder, NativeEndian}; use netlink_packet_utils::{ nla::{DefaultNla, Nla, NlaBuffer, NlasIterator, NLA_F_NESTED}, parsers::{parse_u32, parse_u8}, DecodeError, Emitable, Parseable, }; use crate::{EthtoolAttr, EthtoolHeader}; const ETHTOOL_A_COALESCE_HEADER: u16 = 1; const ETHTOOL_A_COALESCE_RX_USECS: u16 = 2; const ETHTOOL_A_COALESCE_RX_MAX_FRAMES: u16 = 3; const ETHTOOL_A_COALESCE_RX_USECS_IRQ: u16 = 4; const ETHTOOL_A_COALESCE_RX_MAX_FRAMES_IRQ: u16 = 5; const ETHTOOL_A_COALESCE_TX_USECS: u16 = 6; const ETHTOOL_A_COALESCE_TX_MAX_FRAMES: u16 = 7; const ETHTOOL_A_COALESCE_TX_USECS_IRQ: u16 = 8; const ETHTOOL_A_COALESCE_TX_MAX_FRAMES_IRQ: u16 = 9; const ETHTOOL_A_COALESCE_STATS_BLOCK_USECS: u16 = 10; const ETHTOOL_A_COALESCE_USE_ADAPTIVE_RX: u16 = 11; const ETHTOOL_A_COALESCE_USE_ADAPTIVE_TX: u16 = 12; const ETHTOOL_A_COALESCE_PKT_RATE_LOW: u16 = 13; const ETHTOOL_A_COALESCE_RX_USECS_LOW: u16 = 14; const ETHTOOL_A_COALESCE_RX_MAX_FRAMES_LOW: u16 = 15; const ETHTOOL_A_COALESCE_TX_USECS_LOW: u16 = 16; const ETHTOOL_A_COALESCE_TX_MAX_FRAMES_LOW: u16 = 17; const ETHTOOL_A_COALESCE_PKT_RATE_HIGH: u16 = 18; const ETHTOOL_A_COALESCE_RX_USECS_HIGH: u16 = 19; const ETHTOOL_A_COALESCE_RX_MAX_FRAMES_HIGH: u16 = 20; const ETHTOOL_A_COALESCE_TX_USECS_HIGH: u16 = 21; const ETHTOOL_A_COALESCE_TX_MAX_FRAMES_HIGH: u16 = 22; const ETHTOOL_A_COALESCE_RATE_SAMPLE_INTERVAL: u16 = 23; #[derive(Debug, PartialEq, Eq, Clone)] pub enum EthtoolCoalesceAttr { Header(Vec), RxUsecs(u32), RxMaxFrames(u32), RxUsecsIrq(u32), RxMaxFramesIrq(u32), TxUsecs(u32), TxMaxFrames(u32), TxUsecsIrq(u32), TxMaxFramesIrq(u32), StatsBlockUsecs(u32), UseAdaptiveRx(bool), UseAdaptiveTx(bool), PktRateLow(u32), RxUsecsLow(u32), RxMaxFramesLow(u32), TxUsecsLow(u32), TxMaxFramesLow(u32), PktRateHigh(u32), RxUsecsHigh(u32), RxMaxFramesHigh(u32), TxUsecsHigh(u32), TxMaxFramesHigh(u32), RateSampleInterval(u32), Other(DefaultNla), } impl Nla for EthtoolCoalesceAttr { fn value_len(&self) -> usize { match self { Self::Header(hdrs) => hdrs.as_slice().buffer_len(), Self::RxUsecs(_) | Self::RxMaxFrames(_) | Self::RxUsecsIrq(_) | Self::RxMaxFramesIrq(_) | Self::TxUsecs(_) | Self::TxMaxFrames(_) | Self::TxUsecsIrq(_) | Self::TxMaxFramesIrq(_) | Self::StatsBlockUsecs(_) | Self::PktRateLow(_) | Self::RxUsecsLow(_) | Self::RxMaxFramesLow(_) | Self::TxUsecsLow(_) | Self::TxMaxFramesLow(_) | Self::PktRateHigh(_) | Self::RxUsecsHigh(_) | Self::RxMaxFramesHigh(_) | Self::TxUsecsHigh(_) | Self::TxMaxFramesHigh(_) | Self::RateSampleInterval(_) => 4, Self::UseAdaptiveRx(_) | Self::UseAdaptiveTx(_) => 1, Self::Other(attr) => attr.value_len(), } } fn kind(&self) -> u16 { match self { Self::Header(_) => ETHTOOL_A_COALESCE_HEADER | NLA_F_NESTED, Self::RxUsecs(_) => ETHTOOL_A_COALESCE_RX_USECS, Self::RxMaxFrames(_) => ETHTOOL_A_COALESCE_RX_MAX_FRAMES, Self::RxUsecsIrq(_) => ETHTOOL_A_COALESCE_RX_USECS_IRQ, Self::RxMaxFramesIrq(_) => ETHTOOL_A_COALESCE_RX_MAX_FRAMES_IRQ, Self::TxUsecs(_) => ETHTOOL_A_COALESCE_TX_USECS, Self::TxMaxFrames(_) => ETHTOOL_A_COALESCE_TX_MAX_FRAMES, Self::TxUsecsIrq(_) => ETHTOOL_A_COALESCE_TX_USECS_IRQ, Self::TxMaxFramesIrq(_) => ETHTOOL_A_COALESCE_TX_MAX_FRAMES_IRQ, Self::StatsBlockUsecs(_) => ETHTOOL_A_COALESCE_STATS_BLOCK_USECS, Self::UseAdaptiveRx(_) => ETHTOOL_A_COALESCE_USE_ADAPTIVE_RX, Self::UseAdaptiveTx(_) => ETHTOOL_A_COALESCE_USE_ADAPTIVE_TX, Self::PktRateLow(_) => ETHTOOL_A_COALESCE_PKT_RATE_LOW, Self::RxUsecsLow(_) => ETHTOOL_A_COALESCE_RX_USECS_LOW, Self::RxMaxFramesLow(_) => ETHTOOL_A_COALESCE_RX_MAX_FRAMES_LOW, Self::TxUsecsLow(_) => ETHTOOL_A_COALESCE_TX_USECS_LOW, Self::TxMaxFramesLow(_) => ETHTOOL_A_COALESCE_TX_MAX_FRAMES_LOW, Self::PktRateHigh(_) => ETHTOOL_A_COALESCE_PKT_RATE_HIGH, Self::RxUsecsHigh(_) => ETHTOOL_A_COALESCE_RX_USECS_HIGH, Self::RxMaxFramesHigh(_) => ETHTOOL_A_COALESCE_RX_MAX_FRAMES_HIGH, Self::TxUsecsHigh(_) => ETHTOOL_A_COALESCE_TX_USECS_HIGH, Self::TxMaxFramesHigh(_) => ETHTOOL_A_COALESCE_TX_MAX_FRAMES_HIGH, Self::RateSampleInterval(_) => { ETHTOOL_A_COALESCE_RATE_SAMPLE_INTERVAL } Self::Other(attr) => attr.kind(), } } fn emit_value(&self, buffer: &mut [u8]) { match self { Self::Header(ref nlas) => nlas.as_slice().emit(buffer), Self::Other(ref attr) => attr.emit(buffer), Self::RxUsecs(d) | Self::RxMaxFrames(d) | Self::RxUsecsIrq(d) | Self::RxMaxFramesIrq(d) | Self::TxUsecs(d) | Self::TxMaxFrames(d) | Self::TxUsecsIrq(d) | Self::TxMaxFramesIrq(d) | Self::StatsBlockUsecs(d) | Self::PktRateLow(d) | Self::RxUsecsLow(d) | Self::RxMaxFramesLow(d) | Self::TxUsecsLow(d) | Self::TxMaxFramesLow(d) | Self::PktRateHigh(d) | Self::RxUsecsHigh(d) | Self::RxMaxFramesHigh(d) | Self::TxUsecsHigh(d) | Self::TxMaxFramesHigh(d) | Self::RateSampleInterval(d) => { NativeEndian::write_u32(buffer, *d) } Self::UseAdaptiveRx(d) | Self::UseAdaptiveTx(d) => { buffer[0] = (*d).into() } } } } impl<'a, T: AsRef<[u8]> + ?Sized> Parseable> for EthtoolCoalesceAttr { fn parse(buf: &NlaBuffer<&'a T>) -> Result { let payload = buf.value(); Ok(match buf.kind() { ETHTOOL_A_COALESCE_HEADER => { let mut nlas = Vec::new(); let error_msg = "failed to parse coalesce header attributes"; for nla in NlasIterator::new(payload) { let nla = &nla.context(error_msg)?; let parsed = EthtoolHeader::parse(nla).context(error_msg)?; nlas.push(parsed); } Self::Header(nlas) } ETHTOOL_A_COALESCE_RX_USECS => Self::RxUsecs( parse_u32(payload) .context("Invalid ETHTOOL_A_COALESCE_RX_USECS value")?, ), ETHTOOL_A_COALESCE_RX_MAX_FRAMES => { Self::RxMaxFrames(parse_u32(payload).context( "Invalid ETHTOOL_A_COALESCE_RX_MAX_FRAMES value", )?) } ETHTOOL_A_COALESCE_RX_USECS_IRQ => Self::RxUsecsIrq( parse_u32(payload) .context("Invalid ETHTOOL_A_COALESCE_RX_USECS_IRQ value")?, ), ETHTOOL_A_COALESCE_RX_MAX_FRAMES_IRQ => { Self::RxMaxFramesIrq(parse_u32(payload).context( "Invalid ETHTOOL_A_COALESCE_RX_MAX_FRAMES_IRQ value", )?) } ETHTOOL_A_COALESCE_TX_USECS => Self::TxUsecs( parse_u32(payload) .context("Invalid ETHTOOL_A_COALESCE_TX_USECS value")?, ), ETHTOOL_A_COALESCE_TX_MAX_FRAMES => { Self::TxMaxFrames(parse_u32(payload).context( "Invalid ETHTOOL_A_COALESCE_TX_MAX_FRAMES value", )?) } ETHTOOL_A_COALESCE_TX_USECS_IRQ => Self::TxUsecsIrq( parse_u32(payload) .context("Invalid ETHTOOL_A_COALESCE_TX_USECS_IRQ value")?, ), ETHTOOL_A_COALESCE_TX_MAX_FRAMES_IRQ => { Self::TxMaxFramesIrq(parse_u32(payload).context( "Invalid ETHTOOL_A_COALESCE_TX_MAX_FRAMES_IRQ value", )?) } ETHTOOL_A_COALESCE_STATS_BLOCK_USECS => { Self::StatsBlockUsecs(parse_u32(payload).context( "Invalid ETHTOOL_A_COALESCE_STATS_BLOCK_USECS value", )?) } ETHTOOL_A_COALESCE_USE_ADAPTIVE_RX => Self::UseAdaptiveRx( parse_u8(payload).context( "Invalid ETHTOOL_A_COALESCE_USE_ADAPTIVE_RX value", )? == 1, ), ETHTOOL_A_COALESCE_USE_ADAPTIVE_TX => Self::UseAdaptiveTx( parse_u8(payload).context( "Invalid ETHTOOL_A_COALESCE_USE_ADAPTIVE_TX value", )? == 1, ), ETHTOOL_A_COALESCE_PKT_RATE_LOW => Self::PktRateLow( parse_u32(payload) .context("Invalid ETHTOOL_A_COALESCE_PKT_RATE_LOW value")?, ), ETHTOOL_A_COALESCE_RX_USECS_LOW => Self::RxUsecsLow( parse_u32(payload) .context("Invalid ETHTOOL_A_COALESCE_RX_USECS_LOW value")?, ), ETHTOOL_A_COALESCE_RX_MAX_FRAMES_LOW => { Self::RxMaxFramesLow(parse_u32(payload).context( "Invalid ETHTOOL_A_COALESCE_RX_MAX_FRAMES_LOW value", )?) } ETHTOOL_A_COALESCE_TX_USECS_LOW => Self::TxUsecsLow( parse_u32(payload) .context("Invalid ETHTOOL_A_COALESCE_TX_USECS_LOW value")?, ), ETHTOOL_A_COALESCE_TX_MAX_FRAMES_LOW => { Self::TxMaxFramesLow(parse_u32(payload).context( "Invalid ETHTOOL_A_COALESCE_TX_MAX_FRAMES_LOW value", )?) } ETHTOOL_A_COALESCE_PKT_RATE_HIGH => { Self::PktRateHigh(parse_u32(payload).context( "Invalid ETHTOOL_A_COALESCE_PKT_RATE_HIGH value", )?) } ETHTOOL_A_COALESCE_RX_USECS_HIGH => { Self::RxUsecsHigh(parse_u32(payload).context( "Invalid ETHTOOL_A_COALESCE_RX_USECS_HIGH value", )?) } ETHTOOL_A_COALESCE_RX_MAX_FRAMES_HIGH => { Self::RxMaxFramesHigh(parse_u32(payload).context( "Invalid ETHTOOL_A_COALESCE_RX_MAX_FRAMES_HIGH value", )?) } ETHTOOL_A_COALESCE_TX_USECS_HIGH => { Self::TxUsecsHigh(parse_u32(payload).context( "Invalid ETHTOOL_A_COALESCE_TX_USECS_HIGH value", )?) } ETHTOOL_A_COALESCE_TX_MAX_FRAMES_HIGH => { Self::TxMaxFramesHigh(parse_u32(payload).context( "Invalid ETHTOOL_A_COALESCE_TX_MAX_FRAMES_HIGH value", )?) } ETHTOOL_A_COALESCE_RATE_SAMPLE_INTERVAL => { Self::RateSampleInterval(parse_u32(payload).context( "Invalid ETHTOOL_A_COALESCE_RATE_SAMPLE_INTERVAL value", )?) } _ => Self::Other( DefaultNla::parse(buf).context("invalid NLA (unknown kind)")?, ), }) } } pub(crate) fn parse_coalesce_nlas( buffer: &[u8], ) -> Result, DecodeError> { let mut nlas = Vec::new(); for nla in NlasIterator::new(buffer) { let error_msg = format!( "Failed to parse ethtool coalesce message attribute {nla:?}" ); let nla = &nla.context(error_msg.clone())?; let parsed = EthtoolCoalesceAttr::parse(nla).context(error_msg)?; nlas.push(EthtoolAttr::Coalesce(parsed)); } Ok(nlas) } ethtool-0.2.5/src/coalesce/get.rs000064400000000000000000000016501046102023000150020ustar 00000000000000// SPDX-License-Identifier: MIT use futures::TryStream; use netlink_packet_generic::GenlMessage; use crate::{ethtool_execute, EthtoolError, EthtoolHandle, EthtoolMessage}; pub struct EthtoolCoalesceGetRequest { handle: EthtoolHandle, iface_name: Option, } impl EthtoolCoalesceGetRequest { pub(crate) fn new(handle: EthtoolHandle, iface_name: Option<&str>) -> Self { EthtoolCoalesceGetRequest { handle, iface_name: iface_name.map(|i| i.to_string()), } } pub async fn execute( self, ) -> impl TryStream, Error = EthtoolError> { let EthtoolCoalesceGetRequest { mut handle, iface_name, } = self; let ethtool_msg = EthtoolMessage::new_coalesce_get(iface_name.as_deref()); ethtool_execute(&mut handle, iface_name.is_none(), ethtool_msg).await } } ethtool-0.2.5/src/coalesce/handle.rs000064400000000000000000000010321046102023000154500ustar 00000000000000// SPDX-License-Identifier: MIT use crate::{EthtoolCoalesceGetRequest, EthtoolHandle}; pub struct EthtoolCoalesceHandle(EthtoolHandle); impl EthtoolCoalesceHandle { pub fn new(handle: EthtoolHandle) -> Self { EthtoolCoalesceHandle(handle) } /// Retrieve the ethtool coalesces of a interface (equivalent to `ethtool -c /// eth1`) pub fn get( &mut self, iface_name: Option<&str>, ) -> EthtoolCoalesceGetRequest { EthtoolCoalesceGetRequest::new(self.0.clone(), iface_name) } } ethtool-0.2.5/src/coalesce/mod.rs000064400000000000000000000003361046102023000150020ustar 00000000000000// SPDX-License-Identifier: MIT mod attr; mod get; mod handle; pub(crate) use attr::parse_coalesce_nlas; pub use attr::EthtoolCoalesceAttr; pub use get::EthtoolCoalesceGetRequest; pub use handle::EthtoolCoalesceHandle; ethtool-0.2.5/src/connection.rs000064400000000000000000000016311046102023000146030ustar 00000000000000// SPDX-License-Identifier: MIT use std::io; use futures::channel::mpsc::UnboundedReceiver; use genetlink::message::RawGenlMessage; use netlink_packet_core::NetlinkMessage; use netlink_proto::Connection; use netlink_sys::{AsyncSocket, SocketAddr}; use crate::EthtoolHandle; #[cfg(feature = "tokio_socket")] #[allow(clippy::type_complexity)] pub fn new_connection() -> io::Result<( Connection, EthtoolHandle, UnboundedReceiver<(NetlinkMessage, SocketAddr)>, )> { new_connection_with_socket() } #[allow(clippy::type_complexity)] pub fn new_connection_with_socket() -> io::Result<( Connection, EthtoolHandle, UnboundedReceiver<(NetlinkMessage, SocketAddr)>, )> where S: AsyncSocket, { let (conn, handle, messages) = genetlink::new_connection_with_socket()?; Ok((conn, EthtoolHandle::new(handle), messages)) } ethtool-0.2.5/src/error.rs000064400000000000000000000011111046102023000135660ustar 00000000000000// SPDX-License-Identifier: MIT use thiserror::Error; use netlink_packet_core::{ErrorMessage, NetlinkMessage}; use netlink_packet_generic::GenlMessage; use crate::EthtoolMessage; #[derive(Clone, Eq, PartialEq, Debug, Error)] pub enum EthtoolError { #[error("Received an unexpected message {0:?}")] UnexpectedMessage(NetlinkMessage>), #[error("Received a netlink error message {0}")] NetlinkError(ErrorMessage), #[error("A netlink request failed")] RequestFailed(String), #[error("A bug in this crate")] Bug(String), } ethtool-0.2.5/src/feature/attr.rs000064400000000000000000000201411046102023000150460ustar 00000000000000// SPDX-License-Identifier: MIT use anyhow::Context; use log::warn; use netlink_packet_utils::{ nla::{DefaultNla, Nla, NlaBuffer, NlasIterator, NLA_F_NESTED}, parsers::{parse_string, parse_u32}, DecodeError, Emitable, Parseable, }; use crate::{EthtoolAttr, EthtoolHeader}; const ETHTOOL_A_FEATURES_HEADER: u16 = 1; const ETHTOOL_A_FEATURES_HW: u16 = 2; // User changable features const ETHTOOL_A_FEATURES_WANTED: u16 = 3; // User requested fatures const ETHTOOL_A_FEATURES_ACTIVE: u16 = 4; // Active features const ETHTOOL_A_FEATURES_NOCHANGE: u16 = 5; const ETHTOOL_A_BITSET_BITS: u16 = 3; const ETHTOOL_A_BITSET_BITS_BIT: u16 = 1; const ETHTOOL_A_BITSET_BIT_INDEX: u16 = 1; const ETHTOOL_A_BITSET_BIT_NAME: u16 = 2; const ETHTOOL_A_BITSET_BIT_VALUE: u16 = 3; #[derive(Debug, PartialEq, Eq, Clone)] pub struct EthtoolFeatureBit { pub index: u32, pub name: String, pub value: bool, } impl EthtoolFeatureBit { fn new(has_mask: bool) -> Self { Self { index: 0, name: "".into(), value: !has_mask, } } } fn feature_bits_len(_feature_bits: &[EthtoolFeatureBit]) -> usize { todo!("Does not support changing ethtool feature yet") } fn feature_bits_emit(_feature_bits: &[EthtoolFeatureBit], _buffer: &mut [u8]) { todo!("Does not support changing ethtool feature yet") } #[derive(Debug, PartialEq, Eq, Clone)] pub enum EthtoolFeatureAttr { Header(Vec), Hw(Vec), Wanted(Vec), Active(Vec), NoChange(Vec), Other(DefaultNla), } impl Nla for EthtoolFeatureAttr { fn value_len(&self) -> usize { match self { Self::Header(hdrs) => hdrs.as_slice().buffer_len(), Self::Hw(feature_bits) | Self::Wanted(feature_bits) | Self::Active(feature_bits) | Self::NoChange(feature_bits) => { feature_bits_len(feature_bits.as_slice()) } Self::Other(attr) => attr.value_len(), } } fn kind(&self) -> u16 { match self { Self::Header(_) => ETHTOOL_A_FEATURES_HEADER | NLA_F_NESTED, Self::Hw(_) => ETHTOOL_A_FEATURES_HW | NLA_F_NESTED, Self::Wanted(_) => ETHTOOL_A_FEATURES_WANTED | NLA_F_NESTED, Self::Active(_) => ETHTOOL_A_FEATURES_ACTIVE | NLA_F_NESTED, Self::NoChange(_) => ETHTOOL_A_FEATURES_NOCHANGE | NLA_F_NESTED, Self::Other(attr) => attr.kind(), } } fn emit_value(&self, buffer: &mut [u8]) { match self { Self::Header(ref nlas) => nlas.as_slice().emit(buffer), Self::Hw(feature_bits) | Self::Wanted(feature_bits) | Self::Active(feature_bits) | Self::NoChange(feature_bits) => { feature_bits_emit(feature_bits.as_slice(), buffer) } Self::Other(ref attr) => attr.emit(buffer), } } } impl<'a, T: AsRef<[u8]> + ?Sized> Parseable> for EthtoolFeatureAttr { fn parse(buf: &NlaBuffer<&'a T>) -> Result { let payload = buf.value(); Ok(match buf.kind() { ETHTOOL_A_FEATURES_HEADER => { let mut nlas = Vec::new(); let error_msg = "failed to parse feature header attributes"; for nla in NlasIterator::new(payload) { let nla = &nla.context(error_msg)?; let parsed = EthtoolHeader::parse(nla).context(error_msg)?; nlas.push(parsed); } Self::Header(nlas) } ETHTOOL_A_FEATURES_HW => { Self::Hw(parse_bitset_bits_nlas( payload, true, /* ETHTOOL_A_FEATURES_HW is using mask */ )?) } ETHTOOL_A_FEATURES_WANTED => { Self::Wanted(parse_bitset_bits_nlas( payload, false, /* ETHTOOL_A_FEATURES_WANTED does not use mask */ )?) } ETHTOOL_A_FEATURES_ACTIVE => { Self::Active(parse_bitset_bits_nlas( payload, false, /* ETHTOOL_A_FEATURES_ACTIVE does not use mask */ )?) } ETHTOOL_A_FEATURES_NOCHANGE => { Self::NoChange(parse_bitset_bits_nlas( payload, false, /* ETHTOOL_A_FEATURES_NOCHANGE does not use mask */ )?) } _ => Self::Other( DefaultNla::parse(buf).context("invalid NLA (unknown kind)")?, ), }) } } fn parse_bitset_bits_nlas( raw: &[u8], has_mask: bool, ) -> Result, DecodeError> { let error_msg = "failed to parse feature bit sets"; for nla in NlasIterator::new(raw) { let nla = &nla.context(error_msg)?; if nla.kind() == ETHTOOL_A_BITSET_BITS { return parse_bitset_bits_nla(nla.value(), has_mask); } } Err("No ETHTOOL_A_BITSET_BITS NLA found".into()) } fn parse_bitset_bits_nla( raw: &[u8], has_mask: bool, ) -> Result, DecodeError> { let mut feature_bits = Vec::new(); let error_msg = "Failed to parse ETHTOOL_A_BITSET_BITS attributes"; for bit_nla in NlasIterator::new(raw) { let bit_nla = &bit_nla.context(error_msg)?; match bit_nla.kind() { ETHTOOL_A_BITSET_BITS_BIT => { let error_msg = "Failed to parse ETHTOOL_A_BITSET_BITS_BIT attributes"; let nlas = NlasIterator::new(bit_nla.value()); let mut cur_bit_info = EthtoolFeatureBit::new(has_mask); for nla in nlas { let nla = &nla.context(error_msg)?; let payload = nla.value(); match nla.kind() { ETHTOOL_A_BITSET_BIT_INDEX => { if cur_bit_info.index != 0 && !&cur_bit_info.name.is_empty() { feature_bits.push(cur_bit_info); cur_bit_info = EthtoolFeatureBit::new(has_mask); } cur_bit_info.index = parse_u32(payload).context( "Invald ETHTOOL_A_BITSET_BIT_INDEX value", )?; } ETHTOOL_A_BITSET_BIT_NAME => { cur_bit_info.name = parse_string(payload).context( "Invald ETHTOOL_A_BITSET_BIT_NAME value", )?; } ETHTOOL_A_BITSET_BIT_VALUE => { cur_bit_info.value = true; } _ => { warn!( "Unknown ETHTOOL_A_BITSET_BITS_BIT {} {:?}", nla.kind(), nla.value(), ); } } } if cur_bit_info.index != 0 && !&cur_bit_info.name.is_empty() { feature_bits.push(cur_bit_info); } } _ => { warn!( "Unknown ETHTOOL_A_BITSET_BITS kind {}, {:?}", bit_nla.kind(), bit_nla.value() ); } }; } Ok(feature_bits) } pub(crate) fn parse_feature_nlas( buffer: &[u8], ) -> Result, DecodeError> { let mut nlas = Vec::new(); for nla in NlasIterator::new(buffer) { let error_msg = format!( "Failed to parse ethtool feature message attribute {nla:?}" ); let nla = &nla.context(error_msg.clone())?; let parsed = EthtoolFeatureAttr::parse(nla).context(error_msg)?; nlas.push(EthtoolAttr::Feature(parsed)); } Ok(nlas) } ethtool-0.2.5/src/feature/get.rs000064400000000000000000000016431046102023000146610ustar 00000000000000// SPDX-License-Identifier: MIT use futures::TryStream; use netlink_packet_generic::GenlMessage; use crate::{ethtool_execute, EthtoolError, EthtoolHandle, EthtoolMessage}; pub struct EthtoolFeatureGetRequest { handle: EthtoolHandle, iface_name: Option, } impl EthtoolFeatureGetRequest { pub(crate) fn new(handle: EthtoolHandle, iface_name: Option<&str>) -> Self { EthtoolFeatureGetRequest { handle, iface_name: iface_name.map(|i| i.to_string()), } } pub async fn execute( self, ) -> impl TryStream, Error = EthtoolError> { let EthtoolFeatureGetRequest { mut handle, iface_name, } = self; let ethtool_msg = EthtoolMessage::new_feature_get(iface_name.as_deref()); ethtool_execute(&mut handle, iface_name.is_none(), ethtool_msg).await } } ethtool-0.2.5/src/feature/handle.rs000064400000000000000000000010231046102023000153250ustar 00000000000000// SPDX-License-Identifier: MIT use crate::{EthtoolFeatureGetRequest, EthtoolHandle}; pub struct EthtoolFeatureHandle(EthtoolHandle); impl EthtoolFeatureHandle { pub fn new(handle: EthtoolHandle) -> Self { EthtoolFeatureHandle(handle) } /// Retrieve the ethtool features of a interface (equivalent to `ethtool -k /// eth1`) pub fn get( &mut self, iface_name: Option<&str>, ) -> EthtoolFeatureGetRequest { EthtoolFeatureGetRequest::new(self.0.clone(), iface_name) } } ethtool-0.2.5/src/feature/mod.rs000064400000000000000000000003561046102023000146610ustar 00000000000000// SPDX-License-Identifier: MIT mod attr; mod get; mod handle; pub(crate) use attr::parse_feature_nlas; pub use attr::{EthtoolFeatureAttr, EthtoolFeatureBit}; pub use get::EthtoolFeatureGetRequest; pub use handle::EthtoolFeatureHandle; ethtool-0.2.5/src/handle.rs000064400000000000000000000056571046102023000137130ustar 00000000000000// SPDX-License-Identifier: MIT use futures::{future::Either, FutureExt, Stream, StreamExt, TryStream}; use genetlink::GenetlinkHandle; use netlink_packet_core::{ NetlinkMessage, NLM_F_ACK, NLM_F_DUMP, NLM_F_REQUEST, }; use netlink_packet_generic::GenlMessage; use netlink_packet_utils::DecodeError; use crate::{ try_ethtool, EthtoolCoalesceHandle, EthtoolError, EthtoolFeatureHandle, EthtoolLinkModeHandle, EthtoolMessage, EthtoolPauseHandle, EthtoolRingHandle, EthtoolTsInfoHandle, }; #[derive(Clone, Debug)] pub struct EthtoolHandle { pub handle: GenetlinkHandle, } impl EthtoolHandle { pub(crate) fn new(handle: GenetlinkHandle) -> Self { EthtoolHandle { handle } } pub fn pause(&mut self) -> EthtoolPauseHandle { EthtoolPauseHandle::new(self.clone()) } pub fn feature(&mut self) -> EthtoolFeatureHandle { EthtoolFeatureHandle::new(self.clone()) } pub fn link_mode(&mut self) -> EthtoolLinkModeHandle { EthtoolLinkModeHandle::new(self.clone()) } pub fn ring(&mut self) -> EthtoolRingHandle { EthtoolRingHandle::new(self.clone()) } pub fn coalesce(&mut self) -> EthtoolCoalesceHandle { EthtoolCoalesceHandle::new(self.clone()) } pub fn tsinfo(&mut self) -> EthtoolTsInfoHandle { EthtoolTsInfoHandle::new(self.clone()) } pub async fn request( &mut self, message: NetlinkMessage>, ) -> Result< impl Stream< Item = Result< NetlinkMessage>, DecodeError, >, >, EthtoolError, > { self.handle.request(message).await.map_err(|e| { EthtoolError::RequestFailed(format!("BUG: Request failed with {e}")) }) } } pub(crate) async fn ethtool_execute( handle: &mut EthtoolHandle, is_dump: bool, ethtool_msg: EthtoolMessage, ) -> impl TryStream, Error = EthtoolError> { let nl_header_flags = if is_dump { // The NLM_F_ACK is required due to bug of kernel: // https://bugzilla.redhat.com/show_bug.cgi?id=1953847 // without `NLM_F_MULTI`, rust-netlink will not parse // multiple netlink message in single socket reply. // Using NLM_F_ACK will force rust-netlink to parse all till // acked at the end. NLM_F_DUMP | NLM_F_REQUEST | NLM_F_ACK } else { NLM_F_REQUEST }; let mut nl_msg = NetlinkMessage::from(GenlMessage::from_payload(ethtool_msg)); nl_msg.header.flags = nl_header_flags; match handle.request(nl_msg).await { Ok(response) => { Either::Left(response.map(move |msg| Ok(try_ethtool!(msg)))) } Err(e) => Either::Right( futures::future::err::, EthtoolError>( e, ) .into_stream(), ), } } ethtool-0.2.5/src/header.rs000064400000000000000000000057471046102023000137100ustar 00000000000000// SPDX-License-Identifier: MIT use std::ffi::CString; use anyhow::Context; use byteorder::{ByteOrder, NativeEndian}; use netlink_packet_utils::{ nla::{DefaultNla, Nla, NlaBuffer}, parsers::{parse_string, parse_u32}, DecodeError, Parseable, }; const ALTIFNAMSIZ: usize = 128; const ETHTOOL_A_HEADER_DEV_INDEX: u16 = 1; const ETHTOOL_A_HEADER_DEV_NAME: u16 = 2; const ETHTOOL_A_HEADER_FLAGS: u16 = 3; #[derive(Debug, PartialEq, Eq, Clone)] pub enum EthtoolHeader { DevIndex(u32), DevName(String), Flags(u32), Other(DefaultNla), } impl Nla for EthtoolHeader { fn value_len(&self) -> usize { match self { Self::DevIndex(_) | Self::Flags(_) => 4, Self::DevName(s) => { if s.len() + 1 > ALTIFNAMSIZ { ALTIFNAMSIZ } else { s.len() + 1 } } Self::Other(attr) => attr.value_len(), } } fn kind(&self) -> u16 { match self { Self::DevIndex(_) => ETHTOOL_A_HEADER_DEV_INDEX, Self::DevName(_) => ETHTOOL_A_HEADER_DEV_NAME, Self::Flags(_) => ETHTOOL_A_HEADER_FLAGS, Self::Other(attr) => attr.kind(), } } fn emit_value(&self, buffer: &mut [u8]) { match self { Self::DevIndex(value) | Self::Flags(value) => { NativeEndian::write_u32(buffer, *value) } Self::DevName(s) => { str_to_zero_ended_u8_array(s, buffer, ALTIFNAMSIZ) } Self::Other(ref attr) => attr.emit_value(buffer), } } } impl<'a, T: AsRef<[u8]> + ?Sized> Parseable> for EthtoolHeader { fn parse(buf: &NlaBuffer<&'a T>) -> Result { let payload = buf.value(); Ok(match buf.kind() { ETHTOOL_A_HEADER_DEV_INDEX => Self::DevIndex( parse_u32(payload) .context("invalid ETHTOOL_A_HEADER_DEV_INDEX value")?, ), ETHTOOL_A_HEADER_FLAGS => Self::Flags( parse_u32(payload) .context("invalid ETHTOOL_A_HEADER_FLAGS value")?, ), ETHTOOL_A_HEADER_DEV_NAME => Self::DevName( parse_string(payload) .context("invalid ETHTOOL_A_HEADER_DEV_NAME value")?, ), _ => Self::Other( DefaultNla::parse(buf).context("invalid NLA (unknown kind)")?, ), }) } } fn str_to_zero_ended_u8_array( src_str: &str, buffer: &mut [u8], max_size: usize, ) { if let Ok(src_cstring) = CString::new(src_str.as_bytes()) { let src_null_ended_str = src_cstring.into_bytes_with_nul(); if src_null_ended_str.len() > max_size { buffer[..max_size].clone_from_slice(&src_null_ended_str[..max_size]) } else { buffer[..src_null_ended_str.len()] .clone_from_slice(&src_null_ended_str) } } } ethtool-0.2.5/src/lib.rs000064400000000000000000000021511046102023000132100ustar 00000000000000// SPDX-License-Identifier: MIT mod bitset_util; mod coalesce; mod connection; mod error; mod feature; mod handle; mod header; mod link_mode; mod macros; mod message; mod pause; mod ring; mod tsinfo; pub use coalesce::{ EthtoolCoalesceAttr, EthtoolCoalesceGetRequest, EthtoolCoalesceHandle, }; #[cfg(feature = "tokio_socket")] pub use connection::new_connection; pub use connection::new_connection_with_socket; pub use error::EthtoolError; pub use feature::{ EthtoolFeatureAttr, EthtoolFeatureBit, EthtoolFeatureGetRequest, EthtoolFeatureHandle, }; pub use handle::EthtoolHandle; pub use header::EthtoolHeader; pub use link_mode::{ EthtoolLinkModeAttr, EthtoolLinkModeDuplex, EthtoolLinkModeGetRequest, EthtoolLinkModeHandle, }; pub use message::{EthtoolAttr, EthtoolCmd, EthtoolMessage}; pub use pause::{ EthtoolPauseAttr, EthtoolPauseGetRequest, EthtoolPauseHandle, EthtoolPauseStatAttr, }; pub use ring::{EthtoolRingAttr, EthtoolRingGetRequest, EthtoolRingHandle}; pub use tsinfo::{ EthtoolTsInfoAttr, EthtoolTsInfoGetRequest, EthtoolTsInfoHandle, }; pub(crate) use handle::ethtool_execute; ethtool-0.2.5/src/link_mode/attr.rs000064400000000000000000000136701046102023000153650ustar 00000000000000// SPDX-License-Identifier: MIT use anyhow::Context; use netlink_packet_utils::{ nla::{DefaultNla, Nla, NlaBuffer, NlasIterator, NLA_F_NESTED}, parsers::{parse_u32, parse_u8}, DecodeError, Emitable, Parseable, }; use crate::{bitset_util::parse_bitset_bits_nlas, EthtoolAttr, EthtoolHeader}; const ETHTOOL_A_LINKMODES_HEADER: u16 = 1; const ETHTOOL_A_LINKMODES_AUTONEG: u16 = 2; const ETHTOOL_A_LINKMODES_OURS: u16 = 3; const ETHTOOL_A_LINKMODES_PEER: u16 = 4; const ETHTOOL_A_LINKMODES_SPEED: u16 = 5; const ETHTOOL_A_LINKMODES_DUPLEX: u16 = 6; const ETHTOOL_A_LINKMODES_SUBORDINATE_CFG: u16 = 7; const ETHTOOL_A_LINKMODES_SUBORDINATE_STATE: u16 = 8; const ETHTOOL_A_LINKMODES_LANES: u16 = 9; const DUPLEX_HALF: u8 = 0x00; const DUPLEX_FULL: u8 = 0x01; const DUPLEX_UNKNOWN: u8 = 0xff; #[derive(Debug, PartialEq, Eq, Clone)] pub enum EthtoolLinkModeDuplex { Half, Full, Unknown, Other(u8), } impl From for EthtoolLinkModeDuplex { fn from(d: u8) -> Self { match d { DUPLEX_HALF => Self::Half, DUPLEX_FULL => Self::Full, DUPLEX_UNKNOWN => Self::Unknown, _ => Self::Other(d), } } } #[derive(Debug, PartialEq, Eq, Clone)] pub enum EthtoolLinkModeAttr { Header(Vec), Autoneg(bool), Ours(Vec), Peer(Vec), Speed(u32), Duplex(EthtoolLinkModeDuplex), ControllerSubordinateCfg(u8), ControllerSubordinateState(u8), Lanes(u32), Other(DefaultNla), } impl Nla for EthtoolLinkModeAttr { fn value_len(&self) -> usize { match self { Self::Header(hdrs) => hdrs.as_slice().buffer_len(), Self::Autoneg(_) | Self::Duplex(_) | Self::ControllerSubordinateCfg(_) | Self::ControllerSubordinateState(_) => 1, Self::Ours(_) => { todo!("Does not support changing ethtool link mode yet") } Self::Peer(_) => { todo!("Does not support changing ethtool link mode yet") } Self::Speed(_) | Self::Lanes(_) => 4, Self::Other(attr) => attr.value_len(), } } fn kind(&self) -> u16 { match self { Self::Header(_) => ETHTOOL_A_LINKMODES_HEADER | NLA_F_NESTED, Self::Autoneg(_) => ETHTOOL_A_LINKMODES_AUTONEG, Self::Ours(_) => ETHTOOL_A_LINKMODES_OURS, Self::Peer(_) => ETHTOOL_A_LINKMODES_PEER, Self::Speed(_) => ETHTOOL_A_LINKMODES_SPEED, Self::Duplex(_) => ETHTOOL_A_LINKMODES_DUPLEX, Self::ControllerSubordinateCfg(_) => { ETHTOOL_A_LINKMODES_SUBORDINATE_CFG } Self::ControllerSubordinateState(_) => { ETHTOOL_A_LINKMODES_SUBORDINATE_STATE } Self::Lanes(_) => ETHTOOL_A_LINKMODES_LANES, Self::Other(attr) => attr.kind(), } } fn emit_value(&self, buffer: &mut [u8]) { match self { Self::Header(ref nlas) => nlas.as_slice().emit(buffer), Self::Other(ref attr) => attr.emit(buffer), _ => todo!("Does not support changing ethtool link mode yet"), } } } impl<'a, T: AsRef<[u8]> + ?Sized> Parseable> for EthtoolLinkModeAttr { fn parse(buf: &NlaBuffer<&'a T>) -> Result { let payload = buf.value(); Ok(match buf.kind() { ETHTOOL_A_LINKMODES_HEADER => { let mut nlas = Vec::new(); let error_msg = "failed to parse link_mode header attributes"; for nla in NlasIterator::new(payload) { let nla = &nla.context(error_msg)?; let parsed = EthtoolHeader::parse(nla).context(error_msg)?; nlas.push(parsed); } Self::Header(nlas) } ETHTOOL_A_LINKMODES_AUTONEG => Self::Autoneg( parse_u8(payload) .context("Invalid ETHTOOL_A_LINKMODES_AUTONEG value")? == 1, ), ETHTOOL_A_LINKMODES_OURS => { Self::Ours(parse_bitset_bits_nlas(payload)?) } ETHTOOL_A_LINKMODES_PEER => { Self::Peer(parse_bitset_bits_nlas(payload)?) } ETHTOOL_A_LINKMODES_SPEED => Self::Speed( parse_u32(payload) .context("Invalid ETHTOOL_A_LINKMODES_SPEED value")?, ), ETHTOOL_A_LINKMODES_DUPLEX => Self::Duplex( parse_u8(payload) .context("Invalid ETHTOOL_A_LINKMODES_DUPLEX value")? .into(), ), ETHTOOL_A_LINKMODES_SUBORDINATE_CFG => { Self::ControllerSubordinateCfg(parse_u8(payload).context( "Invalid ETHTOOL_A_LINKMODES_SUBORDINATE_CFG value", )?) } ETHTOOL_A_LINKMODES_SUBORDINATE_STATE => { Self::ControllerSubordinateState(parse_u8(payload).context( "Invalid ETHTOOL_A_LINKMODES_SUBORDINATE_STATE value", )?) } ETHTOOL_A_LINKMODES_LANES => Self::Lanes( parse_u32(payload) .context("Invalid ETHTOOL_A_LINKMODES_LANES value")?, ), _ => Self::Other( DefaultNla::parse(buf).context("invalid NLA (unknown kind)")?, ), }) } } pub(crate) fn parse_link_mode_nlas( buffer: &[u8], ) -> Result, DecodeError> { let mut nlas = Vec::new(); for nla in NlasIterator::new(buffer) { let error_msg = format!( "Failed to parse ethtool link_mode message attribute {nla:?}" ); let nla = &nla.context(error_msg.clone())?; let parsed = EthtoolLinkModeAttr::parse(nla).context(error_msg)?; nlas.push(EthtoolAttr::LinkMode(parsed)); } Ok(nlas) } ethtool-0.2.5/src/link_mode/get.rs000064400000000000000000000016511046102023000151660ustar 00000000000000// SPDX-License-Identifier: MIT use futures::TryStream; use netlink_packet_generic::GenlMessage; use crate::{ethtool_execute, EthtoolError, EthtoolHandle, EthtoolMessage}; pub struct EthtoolLinkModeGetRequest { handle: EthtoolHandle, iface_name: Option, } impl EthtoolLinkModeGetRequest { pub(crate) fn new(handle: EthtoolHandle, iface_name: Option<&str>) -> Self { EthtoolLinkModeGetRequest { handle, iface_name: iface_name.map(|i| i.to_string()), } } pub async fn execute( self, ) -> impl TryStream, Error = EthtoolError> { let EthtoolLinkModeGetRequest { mut handle, iface_name, } = self; let ethtool_msg = EthtoolMessage::new_link_mode_get(iface_name.as_deref()); ethtool_execute(&mut handle, iface_name.is_none(), ethtool_msg).await } } ethtool-0.2.5/src/link_mode/handle.rs000064400000000000000000000010251046102023000156350ustar 00000000000000// SPDX-License-Identifier: MIT use crate::{EthtoolHandle, EthtoolLinkModeGetRequest}; pub struct EthtoolLinkModeHandle(EthtoolHandle); impl EthtoolLinkModeHandle { pub fn new(handle: EthtoolHandle) -> Self { EthtoolLinkModeHandle(handle) } /// Retrieve the ethtool link_modes(duplex, link speed and etc) of a /// interface pub fn get( &mut self, iface_name: Option<&str>, ) -> EthtoolLinkModeGetRequest { EthtoolLinkModeGetRequest::new(self.0.clone(), iface_name) } } ethtool-0.2.5/src/link_mode/mod.rs000064400000000000000000000003671046102023000151710ustar 00000000000000// SPDX-License-Identifier: MIT mod attr; mod get; mod handle; pub(crate) use attr::parse_link_mode_nlas; pub use attr::{EthtoolLinkModeAttr, EthtoolLinkModeDuplex}; pub use get::EthtoolLinkModeGetRequest; pub use handle::EthtoolLinkModeHandle; ethtool-0.2.5/src/macros.rs000064400000000000000000000017271046102023000137360ustar 00000000000000// SPDX-License-Identifier: MIT #[macro_export] macro_rules! try_ethtool { ($msg: expr) => {{ use netlink_packet_core::{NetlinkMessage, NetlinkPayload}; use $crate::EthtoolError; match $msg { Ok(msg) => { let (header, payload) = msg.into_parts(); match payload { NetlinkPayload::InnerMessage(msg) => msg, NetlinkPayload::Error(err) => { return Err(EthtoolError::NetlinkError(err)) } _ => { return Err(EthtoolError::UnexpectedMessage( NetlinkMessage::new(header, payload), )) } } } Err(e) => { return Err(EthtoolError::Bug(format!( "BUG: decode error {:?}", e ))) } } }}; } ethtool-0.2.5/src/message.rs000064400000000000000000000207671046102023000141030ustar 00000000000000// SPDX-License-Identifier: MIT use netlink_packet_generic::{GenlFamily, GenlHeader}; use netlink_packet_utils::{ nla::Nla, DecodeError, Emitable, ParseableParametrized, }; use crate::{ coalesce::{parse_coalesce_nlas, EthtoolCoalesceAttr}, feature::{parse_feature_nlas, EthtoolFeatureAttr}, link_mode::{parse_link_mode_nlas, EthtoolLinkModeAttr}, pause::{parse_pause_nlas, EthtoolPauseAttr}, ring::{parse_ring_nlas, EthtoolRingAttr}, tsinfo::{parse_tsinfo_nlas, EthtoolTsInfoAttr}, EthtoolHeader, }; const ETHTOOL_MSG_PAUSE_GET: u8 = 21; const ETHTOOL_MSG_PAUSE_GET_REPLY: u8 = 22; const ETHTOOL_MSG_FEATURES_GET: u8 = 11; const ETHTOOL_MSG_FEATURES_GET_REPLY: u8 = 11; const ETHTOOL_MSG_LINKMODES_GET: u8 = 4; const ETHTOOL_MSG_LINKMODES_GET_REPLY: u8 = 4; const ETHTOOL_MSG_RINGS_GET: u8 = 15; const ETHTOOL_MSG_RINGS_GET_REPLY: u8 = 16; const ETHTOOL_MSG_COALESCE_GET: u8 = 19; const ETHTOOL_MSG_COALESCE_GET_REPLY: u8 = 20; const ETHTOOL_MSG_TSINFO_GET: u8 = 25; const ETHTOOL_MSG_TSINFO_GET_REPLY: u8 = 26; #[derive(Debug, PartialEq, Eq, Clone, Copy)] pub enum EthtoolCmd { PauseGet, PauseGetReply, FeatureGet, FeatureGetReply, LinkModeGet, LinkModeGetReply, RingGet, RingGetReply, CoalesceGet, CoalesceGetReply, TsInfoGet, TsInfoGetReply, } impl From for u8 { fn from(cmd: EthtoolCmd) -> Self { match cmd { EthtoolCmd::PauseGet => ETHTOOL_MSG_PAUSE_GET, EthtoolCmd::PauseGetReply => ETHTOOL_MSG_PAUSE_GET_REPLY, EthtoolCmd::FeatureGet => ETHTOOL_MSG_FEATURES_GET, EthtoolCmd::FeatureGetReply => ETHTOOL_MSG_FEATURES_GET_REPLY, EthtoolCmd::LinkModeGet => ETHTOOL_MSG_LINKMODES_GET, EthtoolCmd::LinkModeGetReply => ETHTOOL_MSG_LINKMODES_GET_REPLY, EthtoolCmd::RingGet => ETHTOOL_MSG_RINGS_GET, EthtoolCmd::RingGetReply => ETHTOOL_MSG_RINGS_GET_REPLY, EthtoolCmd::CoalesceGet => ETHTOOL_MSG_COALESCE_GET, EthtoolCmd::CoalesceGetReply => ETHTOOL_MSG_COALESCE_GET_REPLY, EthtoolCmd::TsInfoGet => ETHTOOL_MSG_TSINFO_GET, EthtoolCmd::TsInfoGetReply => ETHTOOL_MSG_TSINFO_GET_REPLY, } } } #[derive(Debug, PartialEq, Eq, Clone)] pub enum EthtoolAttr { Pause(EthtoolPauseAttr), Feature(EthtoolFeatureAttr), LinkMode(EthtoolLinkModeAttr), Ring(EthtoolRingAttr), Coalesce(EthtoolCoalesceAttr), TsInfo(EthtoolTsInfoAttr), } impl Nla for EthtoolAttr { fn value_len(&self) -> usize { match self { Self::Pause(attr) => attr.value_len(), Self::Feature(attr) => attr.value_len(), Self::LinkMode(attr) => attr.value_len(), Self::Ring(attr) => attr.value_len(), Self::Coalesce(attr) => attr.value_len(), Self::TsInfo(attr) => attr.value_len(), } } fn kind(&self) -> u16 { match self { Self::Pause(attr) => attr.kind(), Self::Feature(attr) => attr.kind(), Self::LinkMode(attr) => attr.kind(), Self::Ring(attr) => attr.kind(), Self::Coalesce(attr) => attr.kind(), Self::TsInfo(attr) => attr.kind(), } } fn emit_value(&self, buffer: &mut [u8]) { match self { Self::Pause(attr) => attr.emit_value(buffer), Self::Feature(attr) => attr.emit_value(buffer), Self::LinkMode(attr) => attr.emit_value(buffer), Self::Ring(attr) => attr.emit_value(buffer), Self::Coalesce(attr) => attr.emit_value(buffer), Self::TsInfo(attr) => attr.emit_value(buffer), } } } #[derive(Debug, PartialEq, Eq, Clone)] pub struct EthtoolMessage { pub cmd: EthtoolCmd, pub nlas: Vec, } impl GenlFamily for EthtoolMessage { fn family_name() -> &'static str { "ethtool" } fn version(&self) -> u8 { 1 } fn command(&self) -> u8 { self.cmd.into() } } impl EthtoolMessage { pub fn new_pause_get(iface_name: Option<&str>) -> Self { let nlas = match iface_name { Some(s) => { vec![EthtoolAttr::Pause(EthtoolPauseAttr::Header(vec![ EthtoolHeader::DevName(s.to_string()), ]))] } None => vec![EthtoolAttr::Pause(EthtoolPauseAttr::Header(vec![]))], }; EthtoolMessage { cmd: EthtoolCmd::PauseGet, nlas, } } pub fn new_feature_get(iface_name: Option<&str>) -> Self { let nlas = match iface_name { Some(s) => { vec![EthtoolAttr::Feature(EthtoolFeatureAttr::Header(vec![ EthtoolHeader::DevName(s.to_string()), ]))] } None => { vec![EthtoolAttr::Feature(EthtoolFeatureAttr::Header(vec![]))] } }; EthtoolMessage { cmd: EthtoolCmd::FeatureGet, nlas, } } pub fn new_link_mode_get(iface_name: Option<&str>) -> Self { let nlas = match iface_name { Some(s) => { vec![EthtoolAttr::LinkMode(EthtoolLinkModeAttr::Header(vec![ EthtoolHeader::DevName(s.to_string()), ]))] } None => { vec![EthtoolAttr::LinkMode(EthtoolLinkModeAttr::Header(vec![]))] } }; EthtoolMessage { cmd: EthtoolCmd::LinkModeGet, nlas, } } pub fn new_ring_get(iface_name: Option<&str>) -> Self { let nlas = match iface_name { Some(s) => vec![EthtoolAttr::Ring(EthtoolRingAttr::Header(vec![ EthtoolHeader::DevName(s.to_string()), ]))], None => vec![EthtoolAttr::Ring(EthtoolRingAttr::Header(vec![]))], }; EthtoolMessage { cmd: EthtoolCmd::RingGet, nlas, } } pub fn new_coalesce_get(iface_name: Option<&str>) -> Self { let nlas = match iface_name { Some(s) => { vec![EthtoolAttr::Coalesce(EthtoolCoalesceAttr::Header(vec![ EthtoolHeader::DevName(s.to_string()), ]))] } None => { vec![EthtoolAttr::Coalesce(EthtoolCoalesceAttr::Header(vec![]))] } }; EthtoolMessage { cmd: EthtoolCmd::CoalesceGet, nlas, } } pub fn new_tsinfo_get(iface_name: Option<&str>) -> Self { let nlas = match iface_name { Some(s) => { vec![EthtoolAttr::TsInfo(EthtoolTsInfoAttr::Header(vec![ EthtoolHeader::DevName(s.to_string()), ]))] } None => { vec![EthtoolAttr::TsInfo(EthtoolTsInfoAttr::Header(vec![]))] } }; EthtoolMessage { cmd: EthtoolCmd::TsInfoGet, nlas, } } } impl Emitable for EthtoolMessage { fn buffer_len(&self) -> usize { self.nlas.as_slice().buffer_len() } fn emit(&self, buffer: &mut [u8]) { self.nlas.as_slice().emit(buffer) } } impl ParseableParametrized<[u8], GenlHeader> for EthtoolMessage { fn parse_with_param( buffer: &[u8], header: GenlHeader, ) -> Result { Ok(match header.cmd { ETHTOOL_MSG_PAUSE_GET_REPLY => Self { cmd: EthtoolCmd::PauseGetReply, nlas: parse_pause_nlas(buffer)?, }, ETHTOOL_MSG_FEATURES_GET_REPLY => Self { cmd: EthtoolCmd::FeatureGetReply, nlas: parse_feature_nlas(buffer)?, }, ETHTOOL_MSG_LINKMODES_GET_REPLY => Self { cmd: EthtoolCmd::LinkModeGetReply, nlas: parse_link_mode_nlas(buffer)?, }, ETHTOOL_MSG_RINGS_GET_REPLY => Self { cmd: EthtoolCmd::RingGetReply, nlas: parse_ring_nlas(buffer)?, }, ETHTOOL_MSG_COALESCE_GET_REPLY => Self { cmd: EthtoolCmd::CoalesceGetReply, nlas: parse_coalesce_nlas(buffer)?, }, ETHTOOL_MSG_TSINFO_GET_REPLY => Self { cmd: EthtoolCmd::TsInfoGetReply, nlas: parse_tsinfo_nlas(buffer)?, }, cmd => { return Err(DecodeError::from(format!( "Unsupported ethtool reply command: {cmd}" ))) } }) } } ethtool-0.2.5/src/pause/attr.rs000064400000000000000000000134041046102023000145340ustar 00000000000000// SPDX-License-Identifier: MIT use anyhow::Context; use byteorder::{ByteOrder, NativeEndian}; use netlink_packet_utils::{ nla::{DefaultNla, Nla, NlaBuffer, NlasIterator, NLA_F_NESTED}, parsers::{parse_u64, parse_u8}, DecodeError, Emitable, Parseable, }; use crate::{EthtoolAttr, EthtoolHeader}; const ETHTOOL_A_PAUSE_HEADER: u16 = 1; const ETHTOOL_A_PAUSE_AUTONEG: u16 = 2; const ETHTOOL_A_PAUSE_RX: u16 = 3; const ETHTOOL_A_PAUSE_TX: u16 = 4; const ETHTOOL_A_PAUSE_STATS: u16 = 5; const ETHTOOL_A_PAUSE_STAT_TX_FRAMES: u16 = 2; const ETHTOOL_A_PAUSE_STAT_RX_FRAMES: u16 = 3; #[derive(Debug, PartialEq, Eq, Clone)] pub enum EthtoolPauseStatAttr { Rx(u64), Tx(u64), Other(DefaultNla), } impl Nla for EthtoolPauseStatAttr { fn value_len(&self) -> usize { match self { Self::Rx(_) | Self::Tx(_) => 8, Self::Other(attr) => attr.value_len(), } } fn kind(&self) -> u16 { match self { Self::Rx(_) => ETHTOOL_A_PAUSE_STAT_RX_FRAMES, Self::Tx(_) => ETHTOOL_A_PAUSE_STAT_RX_FRAMES, Self::Other(attr) => attr.kind(), } } fn emit_value(&self, buffer: &mut [u8]) { match self { Self::Rx(value) | Self::Tx(value) => { NativeEndian::write_u64(buffer, *value) } Self::Other(ref attr) => attr.emit_value(buffer), } } } impl<'a, T: AsRef<[u8]> + ?Sized> Parseable> for EthtoolPauseStatAttr { fn parse(buf: &NlaBuffer<&'a T>) -> Result { let payload = buf.value(); Ok(match buf.kind() { ETHTOOL_A_PAUSE_STAT_TX_FRAMES => Self::Tx( parse_u64(payload) .context("invalid ETHTOOL_A_PAUSE_STAT_TX_FRAMES value")?, ), ETHTOOL_A_PAUSE_STAT_RX_FRAMES => Self::Rx( parse_u64(payload) .context("invalid ETHTOOL_A_PAUSE_STAT_RX_FRAMES value")?, ), _ => Self::Other( DefaultNla::parse(buf).context("invalid NLA (unknown kind)")?, ), }) } } #[derive(Debug, PartialEq, Eq, Clone)] pub enum EthtoolPauseAttr { Header(Vec), AutoNeg(bool), Rx(bool), Tx(bool), Stats(Vec), Other(DefaultNla), } impl Nla for EthtoolPauseAttr { fn value_len(&self) -> usize { match self { Self::Header(hdrs) => hdrs.as_slice().buffer_len(), Self::AutoNeg(_) | Self::Rx(_) | Self::Tx(_) => 1, Self::Stats(ref nlas) => nlas.as_slice().buffer_len(), Self::Other(attr) => attr.value_len(), } } fn kind(&self) -> u16 { match self { Self::Header(_) => ETHTOOL_A_PAUSE_HEADER | NLA_F_NESTED, Self::AutoNeg(_) => ETHTOOL_A_PAUSE_AUTONEG, Self::Rx(_) => ETHTOOL_A_PAUSE_RX, Self::Tx(_) => ETHTOOL_A_PAUSE_TX, Self::Stats(_) => ETHTOOL_A_PAUSE_STATS | NLA_F_NESTED, Self::Other(attr) => attr.kind(), } } fn emit_value(&self, buffer: &mut [u8]) { match self { Self::Header(ref nlas) => nlas.as_slice().emit(buffer), Self::AutoNeg(value) | Self::Rx(value) | Self::Tx(value) => { buffer[0] = *value as u8 } Self::Stats(ref nlas) => nlas.as_slice().emit(buffer), Self::Other(ref attr) => attr.emit(buffer), } } } impl<'a, T: AsRef<[u8]> + ?Sized> Parseable> for EthtoolPauseAttr { fn parse(buf: &NlaBuffer<&'a T>) -> Result { let payload = buf.value(); Ok(match buf.kind() { ETHTOOL_A_PAUSE_HEADER => { let mut nlas = Vec::new(); let error_msg = "failed to parse pause header attributes"; for nla in NlasIterator::new(payload) { let nla = &nla.context(error_msg)?; let parsed = EthtoolHeader::parse(nla).context(error_msg)?; nlas.push(parsed); } Self::Header(nlas) } ETHTOOL_A_PAUSE_AUTONEG => Self::AutoNeg( parse_u8(payload) .context("invalid ETHTOOL_A_PAUSE_AUTONEG value")? == 1, ), ETHTOOL_A_PAUSE_RX => Self::Rx( parse_u8(payload) .context("invalid ETHTOOL_A_PAUSE_RX value")? == 1, ), ETHTOOL_A_PAUSE_TX => Self::Tx( parse_u8(payload) .context("invalid ETHTOOL_A_PAUSE_TX value")? == 1, ), ETHTOOL_A_PAUSE_STATS => { let mut nlas = Vec::new(); let error_msg = "failed to parse pause stats attributes"; for nla in NlasIterator::new(payload) { let nla = &nla.context(error_msg)?; let parsed = EthtoolPauseStatAttr::parse(nla).context(error_msg)?; nlas.push(parsed); } Self::Stats(nlas) } _ => Self::Other( DefaultNla::parse(buf).context("invalid NLA (unknown kind)")?, ), }) } } pub(crate) fn parse_pause_nlas( buffer: &[u8], ) -> Result, DecodeError> { let mut nlas = Vec::new(); for nla in NlasIterator::new(buffer) { let error_msg = format!("Failed to parse ethtool pause message attribute {nla:?}"); let nla = &nla.context(error_msg.clone())?; let parsed = EthtoolPauseAttr::parse(nla).context(error_msg)?; nlas.push(EthtoolAttr::Pause(parsed)); } Ok(nlas) } ethtool-0.2.5/src/pause/get.rs000064400000000000000000000016151046102023000143420ustar 00000000000000// SPDX-License-Identifier: MIT use futures::TryStream; use netlink_packet_generic::GenlMessage; use crate::{ethtool_execute, EthtoolError, EthtoolHandle, EthtoolMessage}; pub struct EthtoolPauseGetRequest { handle: EthtoolHandle, iface_name: Option, } impl EthtoolPauseGetRequest { pub(crate) fn new(handle: EthtoolHandle, iface_name: Option<&str>) -> Self { EthtoolPauseGetRequest { handle, iface_name: iface_name.map(|i| i.to_string()), } } pub async fn execute( self, ) -> impl TryStream, Error = EthtoolError> { let EthtoolPauseGetRequest { mut handle, iface_name, } = self; let ethtool_msg = EthtoolMessage::new_pause_get(iface_name.as_deref()); ethtool_execute(&mut handle, iface_name.is_none(), ethtool_msg).await } } ethtool-0.2.5/src/pause/handle.rs000064400000000000000000000007551046102023000150220ustar 00000000000000// SPDX-License-Identifier: MIT use crate::{EthtoolHandle, EthtoolPauseGetRequest}; pub struct EthtoolPauseHandle(EthtoolHandle); impl EthtoolPauseHandle { pub fn new(handle: EthtoolHandle) -> Self { EthtoolPauseHandle(handle) } /// Retrieve the pause setting of a interface (equivalent to `ethtool -a /// eth1`) pub fn get(&mut self, iface_name: Option<&str>) -> EthtoolPauseGetRequest { EthtoolPauseGetRequest::new(self.0.clone(), iface_name) } } ethtool-0.2.5/src/pause/mod.rs000064400000000000000000000003511046102023000143360ustar 00000000000000// SPDX-License-Identifier: MIT mod attr; mod get; mod handle; pub(crate) use attr::parse_pause_nlas; pub use attr::{EthtoolPauseAttr, EthtoolPauseStatAttr}; pub use get::EthtoolPauseGetRequest; pub use handle::EthtoolPauseHandle; ethtool-0.2.5/src/ring/attr.rs000064400000000000000000000152161046102023000143610ustar 00000000000000// SPDX-License-Identifier: MIT use anyhow::Context; use byteorder::{ByteOrder, NativeEndian}; use netlink_packet_utils::{ nla::{DefaultNla, Nla, NlaBuffer, NlasIterator, NLA_F_NESTED}, parsers::{parse_u32, parse_u8}, DecodeError, Emitable, Parseable, }; use crate::{EthtoolAttr, EthtoolHeader}; const ETHTOOL_A_RINGS_HEADER: u16 = 1; const ETHTOOL_A_RINGS_RX_MAX: u16 = 2; const ETHTOOL_A_RINGS_RX_MINI_MAX: u16 = 3; const ETHTOOL_A_RINGS_RX_JUMBO_MAX: u16 = 4; const ETHTOOL_A_RINGS_TX_MAX: u16 = 5; const ETHTOOL_A_RINGS_RX: u16 = 6; const ETHTOOL_A_RINGS_RX_MINI: u16 = 7; const ETHTOOL_A_RINGS_RX_JUMBO: u16 = 8; const ETHTOOL_A_RINGS_TX: u16 = 9; const ETHTOOL_A_RINGS_RX_BUF_LEN: u16 = 10; const ETHTOOL_A_RINGS_TCP_DATA_SPLIT: u16 = 11; const ETHTOOL_A_RINGS_CQE_SIZE: u16 = 12; const ETHTOOL_A_RINGS_TX_PUSH: u16 = 13; #[derive(Debug, PartialEq, Eq, Clone)] pub enum EthtoolRingAttr { Header(Vec), RxMax(u32), RxMiniMax(u32), RxJumboMax(u32), TxMax(u32), Rx(u32), RxMini(u32), RxJumbo(u32), Tx(u32), RxBufLen(u32), TcpDataSplit(u8), CqeSize(u32), TxPush(bool), Other(DefaultNla), } impl Nla for EthtoolRingAttr { fn value_len(&self) -> usize { match self { Self::Header(hdrs) => hdrs.as_slice().buffer_len(), Self::TcpDataSplit(_) => 1, Self::TxPush(_) => 1, Self::RxMax(_) | Self::RxMiniMax(_) | Self::RxJumboMax(_) | Self::TxMax(_) | Self::Rx(_) | Self::RxMini(_) | Self::RxJumbo(_) | Self::Tx(_) | Self::RxBufLen(_) | Self::CqeSize(_) => 4, Self::Other(attr) => attr.value_len(), } } fn kind(&self) -> u16 { match self { Self::Header(_) => ETHTOOL_A_RINGS_HEADER | NLA_F_NESTED, Self::RxMax(_) => ETHTOOL_A_RINGS_RX_MAX, Self::RxMiniMax(_) => ETHTOOL_A_RINGS_RX_MINI_MAX, Self::RxJumboMax(_) => ETHTOOL_A_RINGS_RX_JUMBO_MAX, Self::TxMax(_) => ETHTOOL_A_RINGS_TX_MAX, Self::Rx(_) => ETHTOOL_A_RINGS_RX, Self::RxMini(_) => ETHTOOL_A_RINGS_RX_MINI, Self::RxJumbo(_) => ETHTOOL_A_RINGS_RX_JUMBO, Self::Tx(_) => ETHTOOL_A_RINGS_TX, Self::RxBufLen(_) => ETHTOOL_A_RINGS_RX_BUF_LEN, Self::TcpDataSplit(_) => ETHTOOL_A_RINGS_TCP_DATA_SPLIT, Self::CqeSize(_) => ETHTOOL_A_RINGS_CQE_SIZE, Self::TxPush(_) => ETHTOOL_A_RINGS_TX_PUSH, Self::Other(attr) => attr.kind(), } } fn emit_value(&self, buffer: &mut [u8]) { match self { Self::Header(ref nlas) => nlas.as_slice().emit(buffer), Self::RxMax(d) | Self::RxMiniMax(d) | Self::RxJumboMax(d) | Self::TxMax(d) | Self::Rx(d) | Self::RxMini(d) | Self::RxJumbo(d) | Self::RxBufLen(d) | Self::CqeSize(d) | Self::Tx(d) => NativeEndian::write_u32(buffer, *d), Self::TcpDataSplit(d) => buffer[0] = *d, Self::TxPush(d) => buffer[0] = *d as u8, Self::Other(ref attr) => attr.emit(buffer), } } } impl<'a, T: AsRef<[u8]> + ?Sized> Parseable> for EthtoolRingAttr { fn parse(buf: &NlaBuffer<&'a T>) -> Result { let payload = buf.value(); Ok(match buf.kind() { ETHTOOL_A_RINGS_HEADER => { let mut nlas = Vec::new(); let error_msg = "failed to parse ring header attributes"; for nla in NlasIterator::new(payload) { let nla = &nla.context(error_msg)?; let parsed = EthtoolHeader::parse(nla).context(error_msg)?; nlas.push(parsed); } Self::Header(nlas) } ETHTOOL_A_RINGS_RX_MAX => Self::RxMax( parse_u32(payload) .context("Invalid ETHTOOL_A_RINGS_RX_MAX value")?, ), ETHTOOL_A_RINGS_RX_MINI_MAX => Self::RxMiniMax( parse_u32(payload) .context("Invalid ETHTOOL_A_RINGS_RX_MINI_MAX value")?, ), ETHTOOL_A_RINGS_RX_JUMBO_MAX => Self::RxJumboMax( parse_u32(payload) .context("Invalid ETHTOOL_A_RINGS_RX_JUMBO_MAX value")?, ), ETHTOOL_A_RINGS_TX_MAX => Self::TxMax( parse_u32(payload) .context("Invalid ETHTOOL_A_RINGS_TX_MAX value")?, ), ETHTOOL_A_RINGS_RX => Self::Rx( parse_u32(payload) .context("Invalid ETHTOOL_A_RINGS_RX value")?, ), ETHTOOL_A_RINGS_RX_MINI => Self::RxMini( parse_u32(payload) .context("Invalid ETHTOOL_A_RINGS_RX_MINI value")?, ), ETHTOOL_A_RINGS_RX_JUMBO => Self::RxJumbo( parse_u32(payload) .context("Invalid ETHTOOL_A_RINGS_RX_JUMBO value")?, ), ETHTOOL_A_RINGS_TX => Self::Tx( parse_u32(payload) .context("Invalid ETHTOOL_A_RINGS_TX value")?, ), ETHTOOL_A_RINGS_RX_BUF_LEN => Self::RxBufLen( parse_u32(payload) .context("Invalid ETHTOOL_A_RINGS_RX_BUF_LEN value")?, ), ETHTOOL_A_RINGS_TCP_DATA_SPLIT => Self::TcpDataSplit( parse_u8(payload) .context("Invalid ETHTOOL_A_RINGS_TCP_DATA_SPLIT value")?, ), ETHTOOL_A_RINGS_CQE_SIZE => Self::CqeSize( parse_u32(payload) .context("Invalid ETHTOOL_A_RINGS_CQE_SIZE value")?, ), ETHTOOL_A_RINGS_TX_PUSH => Self::TxPush( parse_u8(payload) .context("Invalid ETHTOOL_A_RINGS_TX_PUSH value")? > 0, ), kind => Self::Other( DefaultNla::parse(buf) .context(format!("invalid ethtool ring NLA kind {kind}"))?, ), }) } } pub(crate) fn parse_ring_nlas( buffer: &[u8], ) -> Result, DecodeError> { let mut nlas = Vec::new(); for nla in NlasIterator::new(buffer) { let error_msg = format!("Failed to parse ethtool ring message attribute {nla:?}"); let nla = &nla.context(error_msg.clone())?; let parsed = EthtoolRingAttr::parse(nla).context(error_msg)?; nlas.push(EthtoolAttr::Ring(parsed)); } Ok(nlas) } ethtool-0.2.5/src/ring/get.rs000064400000000000000000000016101046102023000141570ustar 00000000000000// SPDX-License-Identifier: MIT use futures::TryStream; use netlink_packet_generic::GenlMessage; use crate::{ethtool_execute, EthtoolError, EthtoolHandle, EthtoolMessage}; pub struct EthtoolRingGetRequest { handle: EthtoolHandle, iface_name: Option, } impl EthtoolRingGetRequest { pub(crate) fn new(handle: EthtoolHandle, iface_name: Option<&str>) -> Self { EthtoolRingGetRequest { handle, iface_name: iface_name.map(|i| i.to_string()), } } pub async fn execute( self, ) -> impl TryStream, Error = EthtoolError> { let EthtoolRingGetRequest { mut handle, iface_name, } = self; let ethtool_msg = EthtoolMessage::new_ring_get(iface_name.as_deref()); ethtool_execute(&mut handle, iface_name.is_none(), ethtool_msg).await } } ethtool-0.2.5/src/ring/handle.rs000064400000000000000000000007471046102023000146450ustar 00000000000000// SPDX-License-Identifier: MIT use crate::{EthtoolHandle, EthtoolRingGetRequest}; pub struct EthtoolRingHandle(EthtoolHandle); impl EthtoolRingHandle { pub fn new(handle: EthtoolHandle) -> Self { EthtoolRingHandle(handle) } /// Retrieve the ethtool rings of a interface (equivalent to `ethtool -g /// eth1`) pub fn get(&mut self, iface_name: Option<&str>) -> EthtoolRingGetRequest { EthtoolRingGetRequest::new(self.0.clone(), iface_name) } } ethtool-0.2.5/src/ring/mod.rs000064400000000000000000000003161046102023000141610ustar 00000000000000// SPDX-License-Identifier: MIT mod attr; mod get; mod handle; pub(crate) use attr::parse_ring_nlas; pub use attr::EthtoolRingAttr; pub use get::EthtoolRingGetRequest; pub use handle::EthtoolRingHandle; ethtool-0.2.5/src/tsinfo/attr.rs000064400000000000000000000075441046102023000147310ustar 00000000000000// SPDX-License-Identifier: MIT use anyhow::Context; use netlink_packet_utils::{ nla::{DefaultNla, Nla, NlaBuffer, NlasIterator, NLA_F_NESTED}, parsers::parse_u32, DecodeError, Emitable, Parseable, }; use crate::{bitset_util::parse_bitset_bits_nlas, EthtoolAttr, EthtoolHeader}; const ETHTOOL_A_TSINFO_HEADER: u16 = 1; const ETHTOOL_A_TSINFO_TIMESTAMPING: u16 = 2; const ETHTOOL_A_TSINFO_TX_TYPES: u16 = 3; const ETHTOOL_A_TSINFO_RX_FILTERS: u16 = 4; const ETHTOOL_A_TSINFO_PHC_INDEX: u16 = 5; #[derive(Debug, PartialEq, Eq, Clone)] pub enum EthtoolTsInfoAttr { Header(Vec), Timestamping(Vec), TxTypes(Vec), RxFilters(Vec), PhcIndex(u32), Other(DefaultNla), } impl Nla for EthtoolTsInfoAttr { fn value_len(&self) -> usize { match self { Self::Header(hdrs) => hdrs.as_slice().buffer_len(), Self::Timestamping(_) | Self::PhcIndex(_) | Self::TxTypes(_) | Self::RxFilters(_) => 4, Self::Other(attr) => attr.value_len(), } } fn kind(&self) -> u16 { match self { Self::Header(_) => ETHTOOL_A_TSINFO_HEADER | NLA_F_NESTED, Self::Timestamping(_) => ETHTOOL_A_TSINFO_TIMESTAMPING, Self::TxTypes(_) => ETHTOOL_A_TSINFO_TX_TYPES, Self::RxFilters(_) => ETHTOOL_A_TSINFO_RX_FILTERS, Self::PhcIndex(_) => ETHTOOL_A_TSINFO_PHC_INDEX, Self::Other(attr) => attr.kind(), } } fn emit_value(&self, buffer: &mut [u8]) { match self { Self::Header(ref nlas) => nlas.as_slice().emit(buffer), Self::Other(ref attr) => attr.emit(buffer), _ => todo!("Does not support changing ethtool ts info yet"), } } } impl<'a, T: AsRef<[u8]> + ?Sized> Parseable> for EthtoolTsInfoAttr { fn parse(buf: &NlaBuffer<&'a T>) -> Result { let payload = buf.value(); Ok(match buf.kind() { ETHTOOL_A_TSINFO_HEADER => { let mut nlas = Vec::new(); let error_msg = "failed to parse link_mode header attributes"; for nla in NlasIterator::new(payload) { let nla = &nla.context(error_msg)?; let parsed = EthtoolHeader::parse(nla).context(error_msg)?; nlas.push(parsed); } Self::Header(nlas) } ETHTOOL_A_TSINFO_TIMESTAMPING => Self::Timestamping( parse_bitset_bits_nlas(payload) .context("Invalid ETHTOOL_A_TSINFO_TIMESTAMPING value")?, ), ETHTOOL_A_TSINFO_TX_TYPES => Self::TxTypes( parse_bitset_bits_nlas(payload) .context("Invalid ETHTOOL_A_TSINFO_TX_TYPES value")?, ), ETHTOOL_A_TSINFO_RX_FILTERS => Self::RxFilters( parse_bitset_bits_nlas(payload) .context("Invalid ETHTOOL_A_TSINFO_RX_FILTERS value")?, ), ETHTOOL_A_TSINFO_PHC_INDEX => Self::PhcIndex( parse_u32(payload) .context("Invalid ETHTOOL_A_TSINFO_PHC_INDEX value")?, ), _ => Self::Other( DefaultNla::parse(buf).context("invalid NLA (unknown kind)")?, ), }) } } pub(crate) fn parse_tsinfo_nlas( buffer: &[u8], ) -> Result, DecodeError> { let mut nlas = Vec::new(); for nla in NlasIterator::new(buffer) { let error_msg = format!( "Failed to parse ethtool tsinfo message attribute {:?}", nla ); let nla = &nla.context(error_msg.clone())?; let parsed = EthtoolTsInfoAttr::parse(nla).context(error_msg)?; nlas.push(EthtoolAttr::TsInfo(parsed)); } Ok(nlas) } ethtool-0.2.5/src/tsinfo/get.rs000064400000000000000000000016221046102023000145250ustar 00000000000000// SPDX-License-Identifier: MIT use futures::TryStream; use netlink_packet_generic::GenlMessage; use crate::{ethtool_execute, EthtoolError, EthtoolHandle, EthtoolMessage}; pub struct EthtoolTsInfoGetRequest { handle: EthtoolHandle, iface_name: Option, } impl EthtoolTsInfoGetRequest { pub(crate) fn new(handle: EthtoolHandle, iface_name: Option<&str>) -> Self { EthtoolTsInfoGetRequest { handle, iface_name: iface_name.map(|i| i.to_string()), } } pub async fn execute( self, ) -> impl TryStream, Error = EthtoolError> { let EthtoolTsInfoGetRequest { mut handle, iface_name, } = self; let ethtool_msg = EthtoolMessage::new_tsinfo_get(iface_name.as_deref()); ethtool_execute(&mut handle, iface_name.is_none(), ethtool_msg).await } } ethtool-0.2.5/src/tsinfo/handle.rs000064400000000000000000000007361046102023000152060ustar 00000000000000// SPDX-License-Identifier: MIT use crate::{EthtoolHandle, EthtoolTsInfoGetRequest}; pub struct EthtoolTsInfoHandle(EthtoolHandle); impl EthtoolTsInfoHandle { pub fn new(handle: EthtoolHandle) -> Self { EthtoolTsInfoHandle(handle) } /// Retrieve the ethtool timestamping capabilities of an interface pub fn get(&mut self, iface_name: Option<&str>) -> EthtoolTsInfoGetRequest { EthtoolTsInfoGetRequest::new(self.0.clone(), iface_name) } } ethtool-0.2.5/src/tsinfo/mod.rs000064400000000000000000000003251046102023000145240ustar 00000000000000// SPDX-License-Identifier: MIT mod attr; mod get; mod handle; pub(crate) use attr::parse_tsinfo_nlas; pub use attr::EthtoolTsInfoAttr; pub use get::EthtoolTsInfoGetRequest; pub use handle::EthtoolTsInfoHandle; ethtool-0.2.5/tests/dump_link_modes.rs000064400000000000000000000016601046102023000161720ustar 00000000000000// SPDX-License-Identifier: MIT use futures::stream::TryStreamExt; #[test] // CI container normally have a veth for external communication which support // link modes of ethtool. fn test_dump_link_modes() { let rt = tokio::runtime::Builder::new_current_thread() .enable_io() .build() .unwrap(); rt.block_on(dump_link_modes()); } async fn dump_link_modes() { let (connection, mut handle, _) = ethtool::new_connection().unwrap(); tokio::spawn(connection); let mut link_modes_handle = handle.link_mode().get(None).execute().await; let mut msgs = Vec::new(); while let Some(msg) = link_modes_handle.try_next().await.unwrap() { msgs.push(msg); } assert!(!msgs.is_empty()); let ethtool_msg = &msgs[0].payload; println!("ethtool_msg {:?}", ðtool_msg); assert!(ethtool_msg.cmd == ethtool::EthtoolCmd::LinkModeGetReply); assert!(ethtool_msg.nlas.len() > 1); } ethtool-0.2.5/tests/get_features_lo.rs000064400000000000000000000021521046102023000161650ustar 00000000000000// SPDX-License-Identifier: MIT use futures::stream::TryStreamExt; #[test] fn test_get_features_of_loopback() { let rt = tokio::runtime::Builder::new_current_thread() .enable_io() .build() .unwrap(); rt.block_on(get_feature(Some("lo"))); } async fn get_feature(iface_name: Option<&str>) { let (connection, mut handle, _) = ethtool::new_connection().unwrap(); tokio::spawn(connection); let mut feature_handle = handle.feature().get(iface_name).execute().await; let mut msgs = Vec::new(); while let Some(msg) = feature_handle.try_next().await.unwrap() { msgs.push(msg); } assert!(msgs.len() == 1); let ethtool_msg = &msgs[0].payload; assert!(ethtool_msg.cmd == ethtool::EthtoolCmd::FeatureGetReply); assert!(ethtool_msg.nlas.len() > 1); assert!( ethtool_msg.nlas[0] == ethtool::EthtoolAttr::Feature( ethtool::EthtoolFeatureAttr::Header(vec![ ethtool::EthtoolHeader::DevIndex(1), ethtool::EthtoolHeader::DevName("lo".into()) ]) ) ); }