diff --git a/src/agent/Cargo.lock b/src/agent/Cargo.lock index 9118d2665f..89578fb9cd 100644 --- a/src/agent/Cargo.lock +++ b/src/agent/Cargo.lock @@ -2,9 +2,9 @@ # It is not intended for manual editing. [[package]] name = "addr2line" -version = "0.13.0" +version = "0.14.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1b6a2d3371669ab3ca9797670853d61402b03d0b4b9ebf33d677dfa720203072" +checksum = "7c0929d69e78dd9bf5408269919fcbcaeb2e35e5d43e5815517cdc6a8e11a423" dependencies = [ "gimli", ] @@ -23,24 +23,24 @@ checksum = "aae1277d39aeec15cb388266ecc24b11c80469deae6067e17a1a7aa9e5c1f234" [[package]] name = "aho-corasick" -version = "0.7.14" +version = "0.7.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b476ce7103678b0c6d3d395dbbae31d48ff910bd28be979ba5d48c6351131d0d" +checksum = "7404febffaa47dac81aa44dba71523c9d069b1bdc50a77db41195149e17f68e5" dependencies = [ "memchr", ] [[package]] name = "anyhow" -version = "1.0.33" +version = "1.0.36" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a1fd36ffbb1fb7c834eac128ea8d0e310c5aeb635548f9d58861e1308d46e71c" +checksum = "68803225a7b13e47191bab76f2687382b60d259e8cf37f6e1893658b84bb9479" [[package]] name = "arc-swap" -version = "0.4.7" +version = "0.4.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4d25d88fd6b8041580a654f9d0c581a047baee2b3efee13275f2fc392fc75034" +checksum = "dabe5a181f83789739c194cbe5a897dde195078fac08568d09221fd6137a7ba8" [[package]] name = "arrayref" @@ -50,9 +50,9 @@ checksum = "a4c527152e37cf757a3f78aae5a06fbeefdb07ccc535c980a3208ee3060dd544" [[package]] name = "arrayvec" -version = "0.5.1" +version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cff77d8686867eceff3105329d4698d96c2391c176d5d03adc90c7389162b5b8" +checksum = "23b62fc65de8e4e7f52534fb52b0f3ed04746ae267519eef2a83941e8085068b" [[package]] name = "async-trait" @@ -61,8 +61,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8d3a45e77e34375a7923b1e8febb049bb011f064714a8e17a1a616fef01da13d" dependencies = [ "proc-macro2 1.0.24", - "quote 1.0.7", - "syn 1.0.45", + "quote 1.0.8", + "syn 1.0.55", ] [[package]] @@ -73,9 +73,9 @@ checksum = "cdb031dd78e28731d87d56cc8ffef4a8f36ca26c38fe2de700543e627f8a464a" [[package]] name = "backtrace" -version = "0.3.53" +version = "0.3.55" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "707b586e0e2f247cbde68cdd2c3ce69ea7b7be43e1c5b426e37c9319c4b9838e" +checksum = "ef5140344c85b01f9bbb4d4b7288a8aa4b3287ccef913a14bcc78a1063623598" dependencies = [ "addr2line", "cfg-if 1.0.0", @@ -87,9 +87,9 @@ dependencies = [ [[package]] name = "base64" -version = "0.12.3" +version = "0.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3441f0f7b02788e948e47f457ca01f1d7e6d92c693bc132c22b087d3141c03ff" +checksum = "904dfeac50f3cdaba28fc6f57fdcddb75f49ed61346676a78c4ffe55877802fd" [[package]] name = "bitflags" @@ -99,9 +99,9 @@ checksum = "cf1de2fe8c75bc145a2f577add951f8134889b4795d47466a54a5c846d691693" [[package]] name = "blake2b_simd" -version = "0.5.10" +version = "0.5.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d8fb2d74254a3a0b5cac33ac9f8ed0e44aa50378d9dbb2e5d83bd21ed1dc2c8a" +checksum = "afa748e348ad3be8263be728124b24a24f268266f6f5d58af9d75f6a40b5c587" dependencies = [ "arrayref", "arrayvec", @@ -114,6 +114,15 @@ version = "1.3.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "08c48aae112d48ed9f069b33538ea9e3e90aa263cfa3d1c24309612b1f7472de" +[[package]] +name = "byteordered" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32687ee8ab498526e3ef07dfbede151650ce202dc83c53494645a24677d89b37" +dependencies = [ + "byteorder", +] + [[package]] name = "bytes" version = "0.4.12" @@ -143,9 +152,9 @@ dependencies = [ [[package]] name = "cc" -version = "1.0.61" +version = "1.0.66" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ed67cbde08356238e75fc4656be4749481eeffb09e19f320a25237d5221c985d" +checksum = "4c0496836a84f8d0495758516b8621a622beb77c0fed418570e50764093ced48" [[package]] name = "cfg-if" @@ -185,15 +194,6 @@ dependencies = [ "winapi 0.3.9", ] -[[package]] -name = "cloudabi" -version = "0.0.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ddfc5b9aa5d4507acaf872de71051dfd0e309860e88966e1051e462a077aac4f" -dependencies = [ - "bitflags", -] - [[package]] name = "constant_time_eq" version = "0.1.5" @@ -202,11 +202,11 @@ checksum = "245097e9a4535ee1e3e3931fcfcd55a796a44c643e8596ff6566d68f09b87bbc" [[package]] name = "crc32fast" -version = "1.2.0" +version = "1.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ba125de2af0df55319f41944744ad91c71113bf74a4646efff39afe1f6842db1" +checksum = "81156fece84ab6a9f2afdb109ce3ae577e42b1228441eded99bd77f627953b1a" dependencies = [ - "cfg-if 0.1.10", + "cfg-if 1.0.0", ] [[package]] @@ -215,7 +215,7 @@ version = "0.4.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b153fe7cbef478c567df0f972e02e6d736db11affe43dfc9c56a9374d1adfb87" dependencies = [ - "crossbeam-utils", + "crossbeam-utils 0.7.2", "maybe-uninit", ] @@ -230,6 +230,17 @@ dependencies = [ "lazy_static", ] +[[package]] +name = "crossbeam-utils" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "02d96d1e189ef58269ebe5b97953da3274d83a93af647c2ddd6f9dab28cedb8d" +dependencies = [ + "autocfg", + "cfg-if 1.0.0", + "lazy_static", +] + [[package]] name = "derive-new" version = "0.5.8" @@ -237,8 +248,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "71f31892cd5c62e414316f2963c5689242c43d8e7bbcaaeca97e5e28c95d91d9" dependencies = [ "proc-macro2 1.0.24", - "quote 1.0.7", - "syn 1.0.45", + "quote 1.0.8", + "syn 1.0.55", ] [[package]] @@ -269,9 +280,9 @@ checksum = "e78d4f1cc4ae33bbfc157ed5d5a5ef3bc29227303d595861deb238fcec4e9457" [[package]] name = "errno" -version = "0.2.6" +version = "0.2.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6eab5ee3df98a279d9b316b1af6ac95422127b1290317e6d18c1743c99418b01" +checksum = "fa68f2fb9cae9d37c9b2b3584aba698a2e97f72d7aef7b9f7aa71d8b54ce46fe" dependencies = [ "errno-dragonfly", "libc", @@ -390,8 +401,8 @@ checksum = "77408a692f1f97bcc61dc001d752e00643408fbc922e4d634c655df50d595556" dependencies = [ "proc-macro-hack", "proc-macro2 1.0.24", - "quote 1.0.7", - "syn 1.0.45", + "quote 1.0.8", + "syn 1.0.55", ] [[package]] @@ -448,15 +459,15 @@ dependencies = [ [[package]] name = "gimli" -version = "0.22.0" +version = "0.23.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "aaf91faf136cb47367fa430cd46e37a788775e7fa104f8b4bcb3861dc389b724" +checksum = "f6503fe142514ca4799d4c26297c4248239fe8838d827db6bd6065c6ed29a6ce" [[package]] name = "heck" -version = "0.3.1" +version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "20564e78d53d2bb135c343b3f47714a56af2061f1c928fdb541dc7b9fdd94205" +checksum = "87cbf45460356b7deeb5e3415b5563308c0a9b057c85e12b06ad551f98d0a6ac" dependencies = [ "unicode-segmentation", ] @@ -498,6 +509,15 @@ dependencies = [ "libc", ] +[[package]] +name = "instant" +version = "0.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "61124eeebbd69b8190558df225adf7e4caafce0d743919e5d6b19652314ec5ec" +dependencies = [ + "cfg-if 1.0.0", +] + [[package]] name = "iovec" version = "0.1.4" @@ -507,6 +527,15 @@ dependencies = [ "libc", ] +[[package]] +name = "ipnetwork" +version = "0.17.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "02c3eaab3ac0ede60ffa41add21970a7df7d91772c03383aac6c2c3d53cc716b" +dependencies = [ + "serde", +] + [[package]] name = "itertools" version = "0.8.2" @@ -530,12 +559,13 @@ dependencies = [ "async-trait", "cgroups-rs", "futures", + "ipnetwork", "lazy_static", "libc", "log", "logging", - "netlink", - "netlink-sys", + "netlink-packet-utils", + "netlink-sys 0.4.0", "nix 0.17.0", "oci", "prctl", @@ -544,6 +574,7 @@ dependencies = [ "protobuf", "protocols", "regex", + "rtnetlink", "rustjail", "scan_fmt", "scopeguard", @@ -576,15 +607,15 @@ checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" [[package]] name = "libc" -version = "0.2.79" +version = "0.2.81" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2448f6066e80e3bfc792e9c98bf705b4b0fc6e8ef5b43e5889aff0eaa9c58743" +checksum = "1482821306169ec4d07f6aca392a4681f66c75c9918aa49641a2595db64053cb" [[package]] name = "libflate" -version = "1.0.2" +version = "1.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e9bac9023e1db29c084f9f8cd9d3852e5e8fddf98fb47c4964a0ea4663d95949" +checksum = "389de7875e06476365974da3e7ff85d55f1972188ccd9f6020dd7c8156e17914" dependencies = [ "adler32", "crc32fast", @@ -600,9 +631,9 @@ checksum = "3286f09f7d4926fc486334f28d8d2e6ebe4f7f9994494b6dab27ddfad2c9b11b" [[package]] name = "lock_api" -version = "0.3.4" +version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c4da24a77a3d8a6d4862d95f72e6fdb9c09a643ecdb402d754004a557f2bec75" +checksum = "dd96ffd135b2fd7b973ac026d28085defbe8983df057ced3eb4f2130b0831312" dependencies = [ "scopeguard", ] @@ -635,9 +666,9 @@ checksum = "60302e4db3a61da70c0cb7991976248362f30319e88850c487b9b95bbf059e00" [[package]] name = "memchr" -version = "2.3.3" +version = "2.3.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3728d817d99e5ac407411fa471ff9800a778d88a24685968b36824eaf4bee400" +checksum = "0ee1c47aaa256ecabcaea351eae4a9b01ef39ed810004e298d2511ed284b1525" [[package]] name = "miniz_oxide" @@ -744,15 +775,56 @@ dependencies = [ ] [[package]] -name = "netlink" -version = "0.1.0" +name = "netlink-packet-core" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "559c08b3ea479ace788a8f75388e3db15210561a4c0f37c4ec7bdf40a3be564e" dependencies = [ + "anyhow", + "byteorder", "libc", - "nix 0.17.0", - "protobuf", - "protocols", - "slog", - "slog-scope", + "netlink-packet-utils", +] + +[[package]] +name = "netlink-packet-route" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2253105e60b35a3fb6cf342b56a45ee1c76ef4b1e68c59b08f813f24c3b7b469" +dependencies = [ + "anyhow", + "bitflags", + "byteorder", + "libc", + "netlink-packet-core", + "netlink-packet-utils", +] + +[[package]] +name = "netlink-packet-utils" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c2afb159d0e3ac700e85f0df25b8438b99d43ed0c0b685242fcdf1b5673e54d" +dependencies = [ + "anyhow", + "byteorder", + "paste", + "thiserror", +] + +[[package]] +name = "netlink-proto" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "31dfd4f1653ba8e1e2410b3def2313f3399d9b9f7ec3a8a6a8f2f670c3e58d71" +dependencies = [ + "bytes 0.5.6", + "futures", + "log", + "netlink-packet-core", + "netlink-sys 0.5.0", + "tokio 0.2.24", + "tokio-util", ] [[package]] @@ -768,6 +840,19 @@ dependencies = [ "tokio 0.2.24", ] +[[package]] +name = "netlink-sys" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bf10c3ab67b9c09b42abb5a53ecb8ffdad160d6485b140a6f21f53ba5362042d" +dependencies = [ + "futures", + "libc", + "log", + "mio 0.6.23", + "tokio 0.2.24", +] + [[package]] name = "nix" version = "0.16.1" @@ -808,13 +893,13 @@ dependencies = [ [[package]] name = "nix" -version = "0.19.0" +version = "0.19.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "85db2feff6bf70ebc3a4793191517d5f0331100a2f10f9bf93b5e5214f32b7b7" +checksum = "b2ccba0cfe4fdf15982d1674c69b1fd80bad427d293849982668dfe454bd61f2" dependencies = [ "bitflags", "cc", - "cfg-if 0.1.10", + "cfg-if 1.0.0", "libc", ] @@ -835,9 +920,9 @@ dependencies = [ [[package]] name = "num-integer" -version = "0.1.43" +version = "0.1.44" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8d59457e662d541ba17869cf51cf177c0b5f0cbf476c66bdc90bf1edac4f875b" +checksum = "d2cc698a63b549a70bc047073d2949cce27cd1c7b0a4a862d08a8031bc2801db" dependencies = [ "autocfg", "num-traits", @@ -845,9 +930,9 @@ dependencies = [ [[package]] name = "num-traits" -version = "0.2.12" +version = "0.2.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ac267bcc07f48ee5f8935ab0d24f316fb722d7a1292e2913f0cc196b29ffd611" +checksum = "9a64b1ec5cda2586e284722486d802acf1f7dbdc623e2bfc57e65ca1cd099290" dependencies = [ "autocfg", ] @@ -864,9 +949,9 @@ dependencies = [ [[package]] name = "object" -version = "0.21.1" +version = "0.22.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "37fd5004feb2ce328a52b0b3d01dbf4ffff72583493900ed15f22d4111c51693" +checksum = "8d3b63360ec3cb337817c2dbd47ab4a0f170d285d8e5a2064600f3def1402397" [[package]] name = "oci" @@ -886,28 +971,35 @@ checksum = "13bd41f508810a131401606d54ac32a467c97172d74ba7662562ebba5ad07fa0" [[package]] name = "parking_lot" -version = "0.10.2" +version = "0.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d3a704eb390aafdc107b0e392f56a82b668e3a71366993b5340f5833fd62505e" +checksum = "6d7744ac029df22dca6284efe4e898991d28e3085c706c972bcd7da4a27a15eb" dependencies = [ + "instant", "lock_api", "parking_lot_core", ] [[package]] name = "parking_lot_core" -version = "0.7.2" +version = "0.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d58c7c768d4ba344e3e8d72518ac13e259d7c7ade24167003b8488e10b6740a3" +checksum = "9ccb628cad4f84851442432c60ad8e1f607e29752d0bf072cbd0baf28aa34272" dependencies = [ - "cfg-if 0.1.10", - "cloudabi", + "cfg-if 1.0.0", + "instant", "libc", "redox_syscall", "smallvec", "winapi 0.3.9", ] +[[package]] +name = "paste" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c5d65c4d95931acda4498f675e332fcbdc9a06705cd07086c510e9b6009cd1c1" + [[package]] name = "path-absolutize" version = "1.2.1" @@ -952,8 +1044,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f8e8d2bf0b23038a4424865103a4df472855692821aab4e4f5c3312d461d9e5f" dependencies = [ "proc-macro2 1.0.24", - "quote 1.0.7", - "syn 1.0.45", + "quote 1.0.8", + "syn 1.0.55", ] [[package]] @@ -976,9 +1068,9 @@ checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" [[package]] name = "ppv-lite86" -version = "0.2.9" +version = "0.2.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c36fa947111f5c62a733b652544dd0016a43ce89619538a8ef92724a6f501a20" +checksum = "ac74c624d6b2d21f425f752262f42188365d7b8ff1aff74c82e45136510a4857" [[package]] name = "prctl" @@ -987,7 +1079,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "059a34f111a9dee2ce1ac2826a68b24601c4298cfeb1a587c3cb493d5ab46f52" dependencies = [ "libc", - "nix 0.19.0", + "nix 0.19.1", ] [[package]] @@ -1161,9 +1253,9 @@ dependencies = [ [[package]] name = "quote" -version = "1.0.7" +version = "1.0.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "aa563d17ecb180e500da1cfd2b028310ac758de548efdd203e18f283af693f37" +checksum = "991431c3519a3f36861882da93630ce66b52918dcf1b8e2fd66b397fc96f28df" dependencies = [ "proc-macro2 1.0.24", ] @@ -1228,9 +1320,9 @@ dependencies = [ [[package]] name = "regex" -version = "1.4.1" +version = "1.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8963b85b8ce3074fecffde43b4b0dded83ce2f367dc8d363afc56679f3ee820b" +checksum = "38cf2c13ed4745de91a5eb834e11c00bcc3709e773173b2ce4c56c9fbde04b9c" dependencies = [ "aho-corasick", "memchr", @@ -1240,9 +1332,9 @@ dependencies = [ [[package]] name = "regex-syntax" -version = "0.6.20" +version = "0.6.21" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8cab7a364d15cde1e505267766a2d3c4e22a843e1a601f0fa7564c0f82ced11c" +checksum = "3b181ba2dcf07aaccad5448e8ead58db5b742cf85dfe035e2227f137a539a189" [[package]] name = "remove_dir_all" @@ -1260,15 +1352,29 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cabe4fa914dec5870285fa7f71f602645da47c486e68486d2b4ceb4a343e90ac" [[package]] -name = "rust-argon2" -version = "0.8.2" +name = "rtnetlink" +version = "0.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9dab61250775933275e84053ac235621dfb739556d5c54a2f2e9313b7cf43a19" +checksum = "0c942df3c7725a0500971d857a080d6dc537e257e19ccb352f80b2c726ef7007" +dependencies = [ + "byteordered", + "futures", + "log", + "netlink-packet-route", + "netlink-proto", + "thiserror", +] + +[[package]] +name = "rust-argon2" +version = "0.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4b18820d944b33caa75a71378964ac46f58517c92b6ae5f762636247c09e78fb" dependencies = [ "base64", "blake2b_simd", "constant_time_eq", - "crossbeam-utils", + "crossbeam-utils 0.8.1", ] [[package]] @@ -1357,26 +1463,26 @@ checksum = "388a1df253eca08550bef6c72392cfe7c30914bf41df5269b68cbd6ff8f570a3" [[package]] name = "serde" -version = "1.0.117" +version = "1.0.118" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b88fa983de7720629c9387e9f517353ed404164b1e482c970a90c1a4aaf7dc1a" +checksum = "06c64263859d87aa2eb554587e2d23183398d617427327cf2b3d0ed8c69e4800" [[package]] name = "serde_derive" -version = "1.0.117" +version = "1.0.118" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cbd1ae72adb44aab48f325a02444a5fc079349a8d804c1fc922aed3f7454c74e" +checksum = "c84d3526699cd55261af4b941e4e725444df67aa4f9e6a3564f18030d12672df" dependencies = [ "proc-macro2 1.0.24", - "quote 1.0.7", - "syn 1.0.45", + "quote 1.0.8", + "syn 1.0.55", ] [[package]] name = "serde_json" -version = "1.0.59" +version = "1.0.60" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dcac07dbffa1c65e7f816ab9eba78eb142c6d44410f4eeba1e26e4f5dfa56b95" +checksum = "1500e84d27fe482ed1dc791a56eddc2f230046a040fa908c08bda1d9fb615779" dependencies = [ "itoa", "ryu", @@ -1385,9 +1491,9 @@ dependencies = [ [[package]] name = "serial_test" -version = "0.5.0" +version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1b15f74add9a9d4a3eb2bf739c9a427d266d3895b53d992c3a7c234fec2ff1f1" +checksum = "e0bccbcf40c8938196944a3da0e133e031a33f4d6b72db3bda3cc556e361905d" dependencies = [ "lazy_static", "parking_lot", @@ -1396,13 +1502,13 @@ dependencies = [ [[package]] name = "serial_test_derive" -version = "0.5.0" +version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "65f59259be9fc1bf677d06cc1456e97756004a1a5a577480f71430bd7c17ba33" +checksum = "b2acd6defeddb41eb60bb468f8825d0cfd0c2a76bc03bfd235b6a1dc4f6a1ad5" dependencies = [ "proc-macro2 1.0.24", - "quote 1.0.7", - "syn 1.0.45", + "quote 1.0.8", + "syn 1.0.55", ] [[package]] @@ -1417,11 +1523,10 @@ dependencies = [ [[package]] name = "signal-hook-registry" -version = "1.2.1" +version = "1.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a3e12110bc539e657a646068aaf5eb5b63af9d0c1f7b29c97113fad80e15f035" +checksum = "ce32ea0c6c56d5eacaeb814fbed9960547021d3edd010ded1425f180536b20ab" dependencies = [ - "arc-swap", "libc", ] @@ -1439,9 +1544,9 @@ checksum = "2f7fb98e76e2022054673f3ebc43a4e12890ec6272530629df6237cafbb70569" [[package]] name = "slog" -version = "2.5.2" +version = "2.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1cc9c640a4adbfbcc11ffb95efe5aa7af7309e002adab54b185507dbf2377b99" +checksum = "8347046d4ebd943127157b94d63abb990fcf729dc4e9978927fdf4ac3c998d06" [[package]] name = "slog-async" @@ -1491,9 +1596,9 @@ dependencies = [ [[package]] name = "smallvec" -version = "1.4.2" +version = "1.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fbee7696b84bbf3d89a1c2eccff0850e3047ed46bfcd2e92c29a2d074d57e252" +checksum = "ae524f056d7d770e174287294f562e95044c68e88dec909a00d2094805db9d75" [[package]] name = "socket2" @@ -1525,12 +1630,12 @@ dependencies = [ [[package]] name = "syn" -version = "1.0.45" +version = "1.0.55" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ea9c5432ff16d6152371f808fb5a871cd67368171b09bb21b43df8e4a47a3556" +checksum = "a571a711dddd09019ccc628e1b17fe87c59b09d513c06c026877aa708334f37a" dependencies = [ "proc-macro2 1.0.24", - "quote 1.0.7", + "quote 1.0.8", "unicode-xid 0.2.1", ] @@ -1556,22 +1661,22 @@ dependencies = [ [[package]] name = "thiserror" -version = "1.0.21" +version = "1.0.22" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "318234ffa22e0920fe9a40d7b8369b5f649d490980cf7aadcf1eb91594869b42" +checksum = "0e9ae34b84616eedaaf1e9dd6026dbe00dcafa92aa0c8077cb69df1fcfe5e53e" dependencies = [ "thiserror-impl", ] [[package]] name = "thiserror-impl" -version = "1.0.21" +version = "1.0.22" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cae2447b6282786c3493999f40a9be2a6ad20cb8bd268b0a0dbf5a065535c0ab" +checksum = "9ba20f23e85b10754cd195504aebf6a27e2e6cbe28c17778a0c930724628dd56" dependencies = [ "proc-macro2 1.0.24", - "quote 1.0.7", - "syn 1.0.45", + "quote 1.0.8", + "syn 1.0.55", ] [[package]] @@ -1636,8 +1741,22 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e44da00bfc73a25f814cd8d7e57a68a5c31b74b3152a0a1d1f590c97ed06265a" dependencies = [ "proc-macro2 1.0.24", - "quote 1.0.7", - "syn 1.0.45", + "quote 1.0.8", + "syn 1.0.55", +] + +[[package]] +name = "tokio-util" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "571da51182ec208780505a32528fc5512a8fe1443ab960b3f2f3ef093cd16930" +dependencies = [ + "bytes 0.5.6", + "futures-core", + "futures-sink", + "log", + "pin-project-lite 0.1.11", + "tokio 0.2.24", ] [[package]] @@ -1704,9 +1823,9 @@ dependencies = [ [[package]] name = "unicode-segmentation" -version = "1.6.0" +version = "1.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e83e153d1053cbb5a118eeff7fd5be06ed99153f00dbcd8ae310c5fb2b22edc0" +checksum = "bb0d2e7be6ae3a5fa87eed5fb451aff96f2573d2694942e40543ae0bbe19c796" [[package]] name = "unicode-xid" diff --git a/src/agent/Cargo.toml b/src/agent/Cargo.toml index cde97aba50..0a6946b057 100644 --- a/src/agent/Cargo.toml +++ b/src/agent/Cargo.toml @@ -9,7 +9,6 @@ oci = { path = "oci" } logging = { path = "../../pkg/logging" } rustjail = { path = "rustjail" } protocols = { path = "protocols" } -netlink = { path = "netlink", features = ["with-log", "with-agent-handler"] } lazy_static = "1.3.0" ttrpc = { version = "0.4.14", features = ["async", "protobuf-codec"], default-features = false } protobuf = "=2.14.0" @@ -27,6 +26,9 @@ tokio = { version = "0.2", features = ["rt-core", "sync", "uds", "stream", "macr futures = "0.3" netlink-sys = { version = "0.4.0", features = ["tokio_socket",]} tokio-vsock = "0.2.2" +rtnetlink = "0.6.0" +netlink-packet-utils = "0.4.0" +ipnetwork = "0.17.0" # slog: # - Dynamic keys required to allow HashMap keys to be slog::Serialized. @@ -48,7 +50,6 @@ cgroups = { package = "cgroups-rs", version = "0.2.1" } [workspace] members = [ - "netlink", "oci", "protocols", "rustjail", diff --git a/src/agent/netlink/Cargo.toml b/src/agent/netlink/Cargo.toml deleted file mode 100644 index a5845fb5f4..0000000000 --- a/src/agent/netlink/Cargo.toml +++ /dev/null @@ -1,20 +0,0 @@ -[package] -name = "netlink" -version = "0.1.0" -authors = ["The Kata Containers community "] -edition = "2018" - -# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html - -[dependencies] -libc = "0.2.58" -nix = "0.17.0" - -protobuf = { version = "=2.14.0", optional = true } -protocols = { path = "../protocols", optional = true } -slog = { version = "2.5.2", features = ["dynamic-keys", "max_level_trace", "release_max_level_info"], optional = true } -slog-scope = { version = "4.1.2", optional = true } - -[features] -with-log = ["slog", "slog-scope"] -with-agent-handler = ["protobuf", "protocols"] diff --git a/src/agent/netlink/src/agent_handler.rs b/src/agent/netlink/src/agent_handler.rs deleted file mode 100644 index a8fffb071f..0000000000 --- a/src/agent/netlink/src/agent_handler.rs +++ /dev/null @@ -1,572 +0,0 @@ -// Copyright (c) 2020 Ant Financial -// Copyright (C) 2020 Alibaba Cloud. All rights reserved. -// -// SPDX-License-Identifier: Apache-2.0 -// - -//! Dedicated Netlink interfaces for Kata agent protocol handler. - -use std::convert::TryFrom; - -use protobuf::RepeatedField; -use protocols::types::{ARPNeighbor, IPAddress, IPFamily, Interface, Route}; - -use super::*; - -#[cfg(feature = "with-log")] -// Convenience macro to obtain the scope logger -macro_rules! sl { - () => { - slog_scope::logger().new(o!("subsystem" => "netlink")) - }; -} - -impl super::RtnlHandle { - pub fn update_interface(&mut self, iface: &Interface) -> Result { - // the reliable way to find link is using hardware address - // as filter. However, hardware filter might not be supported - // by netlink, we may have to dump link list and the find the - // target link. filter using name or family is supported, but - // we cannot use that to find target link. - // let's try if hardware address filter works. -_- - - let ifinfo = self.find_link_by_hwaddr(iface.hwAddr.as_str())?; - - // bring down interface if it is up - if ifinfo.ifi_flags & libc::IFF_UP as u32 != 0 { - self.set_link_status(&ifinfo, false)?; - } - - // delete all addresses associated with the link - let del_addrs: Vec = self.get_link_addresses(&ifinfo)?; - self.delete_all_addrs(&ifinfo, del_addrs.as_ref())?; - - // add new ip addresses in request - for grpc_addr in &iface.IPAddresses { - let rtip = RtIPAddr::try_from(grpc_addr.clone())?; - self.add_one_address(&ifinfo, &rtip)?; - } - - let mut v: Vec = vec![0; DEFAULT_NETLINK_BUF_SIZE]; - // Safe because we have allocated enough buffer space. - let nlh = unsafe { &mut *(v.as_mut_ptr() as *mut nlmsghdr) }; - let ifi = unsafe { &mut *(NLMSG_DATA!(nlh) as *mut ifinfomsg) }; - - // set name, set mtu, IFF_NOARP. in one rtnl_talk. - nlh.nlmsg_len = NLMSG_LENGTH!(mem::size_of::() as u32) as __u32; - nlh.nlmsg_type = RTM_NEWLINK; - nlh.nlmsg_flags = NLM_F_REQUEST; - self.assign_seqnum(nlh); - - ifi.ifi_family = ifinfo.ifi_family; - ifi.ifi_type = ifinfo.ifi_type; - ifi.ifi_index = ifinfo.ifi_index; - if iface.raw_flags & libc::IFF_NOARP as u32 != 0 { - ifi.ifi_change |= libc::IFF_NOARP as u32; - ifi.ifi_flags |= libc::IFF_NOARP as u32; - } - - // Safe because we have allocated enough buffer space. - unsafe { - nlh.addattr32(IFLA_MTU, iface.mtu as u32); - - // if str is null terminated, use addattr_var. - // otherwise, use addattr_str - nlh.addattr_var(IFLA_IFNAME, iface.name.as_ref()); - } - - self.rtnl_talk(v.as_mut_slice(), false)?; - - // TODO: why the result is ignored here? - let _ = self.set_link_status(&ifinfo, true); - - Ok(iface.clone()) - } - - /// Delete this interface/link per request - pub fn remove_interface(&mut self, iface: &Interface) -> Result { - let ifinfo = self.find_link_by_hwaddr(iface.hwAddr.as_str())?; - - self.set_link_status(&ifinfo, false)?; - - let mut v: Vec = vec![0; DEFAULT_NETLINK_BUF_SIZE]; - // Safe because we have allocated enough buffer space. - let nlh = unsafe { &mut *(v.as_mut_ptr() as *mut nlmsghdr) }; - let ifi = unsafe { &mut *(NLMSG_DATA!(nlh) as *mut ifinfomsg) }; - - // No attributes needed? - nlh.nlmsg_len = NLMSG_LENGTH!(mem::size_of::()) as __u32; - nlh.nlmsg_type = RTM_DELLINK; - nlh.nlmsg_flags = NLM_F_REQUEST; - self.assign_seqnum(nlh); - - ifi.ifi_family = ifinfo.ifi_family; - ifi.ifi_index = ifinfo.ifi_index; - ifi.ifi_type = ifinfo.ifi_type; - - self.rtnl_talk(v.as_mut_slice(), false)?; - - Ok(iface.clone()) - } - - pub fn list_interfaces(&mut self) -> Result> { - let mut ifaces: Vec = Vec::new(); - let (_slv, lv) = self.dump_all_links()?; - let (_sav, av) = self.dump_all_addresses(0)?; - - for link in &lv { - // Safe because dump_all_links() returns valid pointers. - let nlh = unsafe { &**link }; - if nlh.nlmsg_type != RTM_NEWLINK && nlh.nlmsg_type != RTM_DELLINK { - continue; - } - - if nlh.nlmsg_len < NLMSG_SPACE!(mem::size_of::()) { - info!( - sl!(), - "invalid nlmsg! nlmsg_len: {}, nlmsg_space: {}", - nlh.nlmsg_len, - NLMSG_SPACE!(mem::size_of::()) - ); - break; - } - - // Safe because we have just validated available buffer space above. - let ifi = unsafe { &*(NLMSG_DATA!(nlh) as *const ifinfomsg) }; - let rta: *mut rtattr = IFLA_RTA!(ifi as *const ifinfomsg) as *mut rtattr; - let rtalen = IFLA_PAYLOAD!(nlh) as u32; - let attrs = unsafe { parse_attrs(rta, rtalen, (IFLA_MAX + 1) as usize)? }; - - // fill out some fields of Interface, - let mut iface: Interface = Interface::default(); - - // Safe because parse_attrs() returns valid pointers. - unsafe { - if !attrs[IFLA_IFNAME as usize].is_null() { - let t = attrs[IFLA_IFNAME as usize]; - iface.name = String::from_utf8(getattr_var(t as *const rtattr))?; - } - - if !attrs[IFLA_MTU as usize].is_null() { - let t = attrs[IFLA_MTU as usize]; - iface.mtu = getattr32(t) as u64; - } - - if !attrs[IFLA_ADDRESS as usize].is_null() { - let alen = RTA_PAYLOAD!(attrs[IFLA_ADDRESS as usize]); - let a: *const u8 = RTA_DATA!(attrs[IFLA_ADDRESS as usize]) as *const u8; - iface.hwAddr = parser::format_address(a, alen as u32)?; - } - } - - // get ip address info from av - let mut ads: Vec = Vec::new(); - for address in &av { - // Safe because dump_all_addresses() returns valid pointers. - let alh = unsafe { &**address }; - if alh.nlmsg_type != RTM_NEWADDR { - continue; - } - - let tlen = NLMSG_SPACE!(mem::size_of::()); - if alh.nlmsg_len < tlen { - info!( - sl!(), - "invalid nlmsg! nlmsg_len: {}, nlmsg_space: {}", alh.nlmsg_len, tlen - ); - break; - } - - // Safe becahse we have checked avialable buffer space by NLMSG_SPACE above. - let ifa = unsafe { &*(NLMSG_DATA!(alh) as *const ifaddrmsg) }; - let arta: *mut rtattr = IFA_RTA!(ifa) as *mut rtattr; - let artalen = IFA_PAYLOAD!(alh) as u32; - - if ifa.ifa_index as u32 == ifi.ifi_index as u32 { - // found target addresses, parse attributes and fill out Interface - let addrs = unsafe { parse_attrs(arta, artalen, (IFA_MAX + 1) as usize)? }; - - // fill address field of Interface - let mut one: IPAddress = IPAddress::default(); - let tattr: *const rtattr = if !addrs[IFA_ADDRESS as usize].is_null() { - addrs[IFA_ADDRESS as usize] - } else { - addrs[IFA_LOCAL as usize] - }; - - one.mask = format!("{}", ifa.ifa_prefixlen); - one.family = IPFamily::v4; - if ifa.ifa_family == libc::AF_INET6 as u8 { - one.family = IPFamily::v6; - } - - // Safe because parse_attrs() returns valid pointers. - unsafe { - let a: *const u8 = RTA_DATA!(tattr) as *const u8; - let alen = RTA_PAYLOAD!(tattr); - one.address = parser::format_address(a, alen as u32)?; - } - - ads.push(one); - } - } - - iface.IPAddresses = RepeatedField::from_vec(ads); - ifaces.push(iface); - } - - Ok(ifaces) - } - - pub fn update_routes(&mut self, rt: &[Route]) -> Result> { - let rs = self.get_all_routes()?; - self.delete_all_routes(&rs)?; - - for grpcroute in rt { - if grpcroute.gateway.as_str() == "" { - let r = RtRoute::try_from(grpcroute.clone())?; - if r.index == -1 { - continue; - } - self.add_one_route(&r)?; - } - } - - for grpcroute in rt { - if grpcroute.gateway.as_str() != "" { - let r = RtRoute::try_from(grpcroute.clone())?; - if r.index == -1 { - continue; - } - self.add_one_route(&r)?; - } - } - - Ok(rt.to_owned()) - } - - pub fn list_routes(&mut self) -> Result> { - // currently, only dump routes from main table for ipv4 - // ie, rtmsg.rtmsg_family = AF_INET, set RT_TABLE_MAIN - // attribute in dump request - // Fix Me: think about othe tables, ipv6.. - let mut rs: Vec = Vec::new(); - let (_srv, rv) = self.dump_all_routes()?; - - // parse out routes and store in rs - for r in &rv { - // Safe because dump_all_routes() returns valid pointers. - let nlh = unsafe { &**r }; - if nlh.nlmsg_type != RTM_NEWROUTE && nlh.nlmsg_type != RTM_DELROUTE { - info!(sl!(), "not route message!"); - continue; - } - let tlen = NLMSG_SPACE!(mem::size_of::()); - if nlh.nlmsg_len < tlen { - info!( - sl!(), - "invalid nlmsg! nlmsg_len: {}, nlmsg_spae: {}", nlh.nlmsg_len, tlen - ); - break; - } - - // Safe because we have just validated available buffer space above. - let rtm = unsafe { &mut *(NLMSG_DATA!(nlh) as *mut rtmsg) }; - if rtm.rtm_table != RT_TABLE_MAIN as u8 { - continue; - } - let rta: *mut rtattr = RTM_RTA!(rtm) as *mut rtattr; - let rtalen = RTM_PAYLOAD!(nlh) as u32; - let attrs = unsafe { parse_attrs(rta, rtalen, (RTA_MAX + 1) as usize)? }; - - let t = attrs[RTA_TABLE as usize]; - if !t.is_null() { - // Safe because parse_attrs() returns valid pointers - let table = unsafe { getattr32(t) }; - if table != RT_TABLE_MAIN { - continue; - } - } - - // find source, destination, gateway, scope, and and device name - let mut t = attrs[RTA_DST as usize]; - let mut rte: Route = Route::default(); - - // Safe because parse_attrs() returns valid pointers - unsafe { - // destination - if !t.is_null() { - let data: *const u8 = RTA_DATA!(t) as *const u8; - let len = RTA_PAYLOAD!(t) as u32; - rte.dest = - format!("{}/{}", parser::format_address(data, len)?, rtm.rtm_dst_len); - } - - // gateway - t = attrs[RTA_GATEWAY as usize]; - if !t.is_null() { - let data: *const u8 = RTA_DATA!(t) as *const u8; - let len = RTA_PAYLOAD!(t) as u32; - rte.gateway = parser::format_address(data, len)?; - - // for gateway, destination is 0.0.0.0 - rte.dest = "0.0.0.0".to_string(); - } - - // source - t = attrs[RTA_SRC as usize]; - if t.is_null() { - t = attrs[RTA_PREFSRC as usize]; - } - if !t.is_null() { - let data: *const u8 = RTA_DATA!(t) as *const u8; - let len = RTA_PAYLOAD!(t) as u32; - rte.source = parser::format_address(data, len)?; - - if rtm.rtm_src_len != 0 { - rte.source = format!("{}/{}", rte.source.as_str(), rtm.rtm_src_len); - } - } - - // scope - rte.scope = rtm.rtm_scope as u32; - - // oif - t = attrs[RTA_OIF as usize]; - if !t.is_null() { - let data = &*(RTA_DATA!(t) as *const i32); - assert_eq!(RTA_PAYLOAD!(t), 4); - - rte.device = self - .get_name_by_index(*data) - .unwrap_or_else(|_| "unknown".to_string()); - } - } - - rs.push(rte); - } - - Ok(rs) - } - - pub fn add_arp_neighbors(&mut self, neighs: &[ARPNeighbor]) -> Result<()> { - for neigh in neighs { - self.add_one_arp_neighbor(&neigh)?; - } - - Ok(()) - } - - pub fn add_one_arp_neighbor(&mut self, neigh: &ARPNeighbor) -> Result<()> { - let to_ip = match neigh.toIPAddress.as_ref() { - None => return nix_errno(Errno::EINVAL), - Some(v) => { - if v.address.is_empty() { - return nix_errno(Errno::EINVAL); - } - v.address.as_ref() - } - }; - - let dev = self.find_link_by_name(&neigh.device)?; - - let mut v: Vec = vec![0; DEFAULT_NETLINK_BUF_SIZE]; - // Safe because we have allocated enough buffer space. - let nlh = unsafe { &mut *(v.as_mut_ptr() as *mut nlmsghdr) }; - let ndm = unsafe { &mut *(NLMSG_DATA!(nlh) as *mut ndmsg) }; - - nlh.nlmsg_len = NLMSG_LENGTH!(std::mem::size_of::()) as u32; - nlh.nlmsg_type = RTM_NEWNEIGH; - nlh.nlmsg_flags = NLM_F_REQUEST | NLM_F_CREATE | NLM_F_EXCL; - self.assign_seqnum(nlh); - - ndm.ndm_family = libc::AF_UNSPEC as __u8; - ndm.ndm_state = IFA_F_PERMANENT as __u16; - // process lladdr - if neigh.lladdr != "" { - let llabuf = parser::parse_mac_addr(&neigh.lladdr)?; - - // Safe because we have allocated enough buffer space. - unsafe { nlh.addattr_var(NDA_LLADDR, llabuf.as_ref()) }; - } - - let (family, ip_data) = parser::parse_ip_addr_with_family(&to_ip)?; - ndm.ndm_family = family; - // Safe because we have allocated enough buffer space. - unsafe { nlh.addattr_var(NDA_DST, ip_data.as_ref()) }; - - // process state - if neigh.state != 0 { - ndm.ndm_state = neigh.state as __u16; - } - - // process flags - ndm.ndm_flags = (*ndm).ndm_flags | neigh.flags as __u8; - - // process dev - ndm.ndm_ifindex = dev.ifi_index; - - // send - self.rtnl_talk(v.as_mut_slice(), false)?; - - Ok(()) - } -} - -impl TryFrom for RtIPAddr { - type Error = nix::Error; - - fn try_from(ipi: IPAddress) -> std::result::Result { - let ip_family = if ipi.family == IPFamily::v4 { - libc::AF_INET - } else { - libc::AF_INET6 - } as __u8; - - let ip_mask = parser::parse_u8(ipi.mask.as_str(), 10)?; - let addr = parser::parse_ip_addr(ipi.address.as_ref())?; - - Ok(Self { - ip_family, - ip_mask, - addr, - }) - } -} - -impl TryFrom for RtRoute { - type Error = nix::Error; - - fn try_from(r: Route) -> std::result::Result { - // only handle ipv4 - - let index = { - let mut rh = RtnlHandle::new(NETLINK_ROUTE, 0)?; - match rh.find_link_by_name(r.device.as_str()) { - Ok(ifi) => ifi.ifi_index, - Err(_) => -1, - } - }; - - let (dest, dst_len) = if r.dest.is_empty() { - (Some(vec![0 as u8; 4]), 0) - } else { - let (dst, mask) = parser::parse_cidr(r.dest.as_str())?; - (Some(dst), mask) - }; - - let (source, src_len) = if r.source.is_empty() { - (None, 0) - } else { - let (src, mask) = parser::parse_cidr(r.source.as_str())?; - (Some(src), mask) - }; - - let gateway = if r.gateway.is_empty() { - None - } else { - Some(parser::parse_ip_addr(r.gateway.as_str())?) - }; - - Ok(Self { - dest, - source, - src_len, - dst_len, - index, - gateway, - scope: r.scope as u8, - protocol: RTPROTO_UNSPEC, - }) - } -} - -#[cfg(test)] -mod tests { - use super::*; - use crate::{RtnlHandle, NETLINK_ROUTE}; - use protocols::types::IPAddress; - use std::process::Command; - - fn clean_env_for_test_add_one_arp_neighbor(dummy_name: &str, ip: &str) { - // ip link delete dummy - Command::new("ip") - .args(&["link", "delete", dummy_name]) - .output() - .expect("prepare: failed to delete dummy"); - - // ip neigh del dev dummy ip - Command::new("ip") - .args(&["neigh", "del", dummy_name, ip]) - .output() - .expect("prepare: failed to delete neigh"); - } - - fn prepare_env_for_test_add_one_arp_neighbor(dummy_name: &str, ip: &str) { - clean_env_for_test_add_one_arp_neighbor(dummy_name, ip); - // modprobe dummy - Command::new("modprobe") - .arg("dummy") - .output() - .expect("failed to run modprobe dummy"); - - // ip link add dummy type dummy - Command::new("ip") - .args(&["link", "add", dummy_name, "type", "dummy"]) - .output() - .expect("failed to add dummy interface"); - - // ip addr add 192.168.0.2/16 dev dummy - Command::new("ip") - .args(&["addr", "add", "192.168.0.2/16", "dev", dummy_name]) - .output() - .expect("failed to add ip for dummy"); - - // ip link set dummy up; - Command::new("ip") - .args(&["link", "set", dummy_name, "up"]) - .output() - .expect("failed to up dummy"); - } - - #[test] - fn test_add_one_arp_neighbor() { - // skip_if_not_root - if !nix::unistd::Uid::effective().is_root() { - println!("INFO: skipping {} which needs root", module_path!()); - return; - } - - let mac = "6a:92:3a:59:70:aa"; - let to_ip = "169.254.1.1"; - let dummy_name = "dummy_for_arp"; - - prepare_env_for_test_add_one_arp_neighbor(dummy_name, to_ip); - - let mut ip_address = IPAddress::new(); - ip_address.set_address(to_ip.to_string()); - - let mut neigh = ARPNeighbor::new(); - neigh.set_toIPAddress(ip_address); - neigh.set_device(dummy_name.to_string()); - neigh.set_lladdr(mac.to_string()); - neigh.set_state(0x80); - - let mut rtnl = RtnlHandle::new(NETLINK_ROUTE, 0).unwrap(); - - rtnl.add_one_arp_neighbor(&neigh).unwrap(); - - // ip neigh show dev dummy ip - let stdout = Command::new("ip") - .args(&["neigh", "show", "dev", dummy_name, to_ip]) - .output() - .expect("failed to show neigh") - .stdout; - - let stdout = std::str::from_utf8(&stdout).expect("failed to conveert stdout"); - - assert_eq!(stdout, format!("{} lladdr {} PERMANENT\n", to_ip, mac)); - - clean_env_for_test_add_one_arp_neighbor(dummy_name, to_ip); - } -} diff --git a/src/agent/netlink/src/lib.rs b/src/agent/netlink/src/lib.rs deleted file mode 100644 index 37268286fa..0000000000 --- a/src/agent/netlink/src/lib.rs +++ /dev/null @@ -1,2354 +0,0 @@ -// Copyright (c) 2019 Ant Financial -// -// SPDX-License-Identifier: Apache-2.0 -// - -//! Structs, consts to support Linux Netlink operations -//! -//! The netlink library makes heavy use of unsafe functions, we trust the Linux kernel and assume: -//! - all pointers/buffers generated by kernel are valid -//! - all offsets are within legal range -//! - all fields have been correctly aligned - -#![allow(non_camel_case_types)] -// NOTE!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! -// By default, there are many warnings for incorrect alignment like: -// casting from `*mut u8` to a more-strictly-aligned pointer (`*mut nlmsghdr`) (1 < 4 bytes) -// -// The root cause is that we use a Vec buffer to receive netlink message from the kernel. -// The data buffer for a Vec may be 1-byte aligned, which doesn't match the alignment -// requirement for netlink message fields. The reason it works without failure now is that -// rust memory allocation is 16-byte aligned by default. -// -// But please do pay attention here, it's no guarantee that it will be 16-byte aligned, just works -// in this way. For safety, we should use something like the FamStruct from the vmm-sys-util crate. -#![allow(clippy::cast_ptr_alignment)] - -extern crate libc; -extern crate nix; - -#[cfg(feature = "with-agent-handler")] -extern crate protobuf; -#[cfg(feature = "with-agent-handler")] -extern crate protocols; - -#[cfg(feature = "with-log")] -#[macro_use] -extern crate slog; -#[cfg(feature = "with-log")] -extern crate slog_scope; - -use nix::errno::Errno; -use std::borrow::Borrow; -use std::ffi::CString; -use std::fmt; -use std::mem; -use std::os::unix::io::RawFd; - -#[cfg(feature = "with-log")] -// Convenience macro to obtain the scope logger -macro_rules! sl { - () => { - slog_scope::logger().new(o!("subsystem" => "netlink")) - }; -} - -#[cfg(not(feature = "with-log"))] -#[macro_export] -macro_rules! info { - ($l:expr, #$tag:expr, $($args:tt)*) => {}; - ($l:expr, $($args:tt)*) => {}; -} - -#[cfg(feature = "with-agent-handler")] -mod agent_handler; -pub mod parser; - -pub const DEFAULT_NETLINK_BUF_SIZE: usize = 2048; - -/// Specialized std::result::Result for Netlink related operations. -pub type Result = std::result::Result; - -pub type __s8 = libc::c_char; -pub type __u8 = libc::c_uchar; -pub type __s16 = libc::c_short; -pub type __u16 = libc::c_ushort; -pub type __s32 = libc::c_int; -pub type __u32 = libc::c_uint; -pub type __s64 = libc::c_longlong; -pub type __u64 = libc::c_ulonglong; - -// we need ifaddrmsg, ifinfomasg, rtmsg -// we need some constant - -pub const RTM_BASE: libc::c_ushort = 16; -pub const RTM_NEWLINK: libc::c_ushort = 16; -pub const RTM_DELLINK: libc::c_ushort = 17; -pub const RTM_GETLINK: libc::c_ushort = 18; -pub const RTM_SETLINK: libc::c_ushort = 19; -pub const RTM_NEWADDR: libc::c_ushort = 20; -pub const RTM_DELADDR: libc::c_ushort = 21; -pub const RTM_GETADDR: libc::c_ushort = 22; -pub const RTM_NEWROUTE: libc::c_ushort = 24; -pub const RTM_DELROUTE: libc::c_ushort = 25; -pub const RTM_GETROUTE: libc::c_ushort = 26; -pub const RTM_NEWNEIGH: libc::c_ushort = 28; -pub const RTM_DELNEIGH: libc::c_ushort = 29; -pub const RTM_GETNEIGH: libc::c_ushort = 30; -pub const RTM_NEWRULE: libc::c_ushort = 32; -pub const RTM_DELRULE: libc::c_ushort = 33; -pub const RTM_GETRULE: libc::c_ushort = 34; -pub const RTM_NEWQDISC: libc::c_ushort = 36; -pub const RTM_DELQDISC: libc::c_ushort = 37; -pub const RTM_GETQDISC: libc::c_ushort = 38; -pub const RTM_NEWTCLASS: libc::c_ushort = 40; -pub const RTM_DELTCLASS: libc::c_ushort = 41; -pub const RTM_GETTCLASS: libc::c_ushort = 42; -pub const RTM_NEWTFILTER: libc::c_ushort = 44; -pub const RTM_DELTFILTER: libc::c_ushort = 45; -pub const RTM_GETTFILTER: libc::c_ushort = 46; -pub const RTM_NEWACTION: libc::c_ushort = 48; -pub const RTM_DELACTION: libc::c_ushort = 49; -pub const RTM_GETACTION: libc::c_ushort = 50; -pub const RTM_NEWPREFIX: libc::c_ushort = 52; -pub const RTM_GETMULTICAST: libc::c_ushort = 58; -pub const RTM_GETANYCAST: libc::c_ushort = 62; -pub const RTM_NEWNEIGHTBL: libc::c_ushort = 64; -pub const RTM_GETNEIGHTBL: libc::c_ushort = 66; -pub const RTM_SETNEIGHTBL: libc::c_ushort = 67; -pub const RTM_NEWNDUSEROPT: libc::c_ushort = 68; -pub const RTM_NEWADDRLABEL: libc::c_ushort = 72; -pub const RTM_DELADDRLABEL: libc::c_ushort = 73; -pub const RTM_GETADDRLABEL: libc::c_ushort = 74; -pub const RTM_GETDCB: libc::c_ushort = 78; -pub const RTM_SETDCB: libc::c_ushort = 79; -pub const RTM_NEWNETCONF: libc::c_ushort = 80; -pub const RTM_GETNETCONF: libc::c_ushort = 82; -pub const RTM_NEWMDB: libc::c_ushort = 84; -pub const RTM_DELMDB: libc::c_ushort = 85; -pub const RTM_GETMDB: libc::c_ushort = 86; -pub const RTM_NEWNSID: libc::c_ushort = 88; -pub const RTM_DELNSID: libc::c_ushort = 89; -pub const RTM_GETNSID: libc::c_ushort = 90; -pub const RTM_NEWSTATS: libc::c_ushort = 92; -pub const RTM_GETSTATS: libc::c_ushort = 94; -pub const RTM_NEWCACHEREPORT: libc::c_ushort = 96; -pub const RTM_NEWCHAIN: libc::c_ushort = 100; -pub const RTM_DELCHAIN: libc::c_ushort = 101; -pub const RTM_GETCHAIN: libc::c_ushort = 102; -pub const __RTM_MAX: libc::c_ushort = 103; - -pub const RTM_MAX: libc::c_ushort = ((__RTM_MAX + 3) & !3) - 1; -pub const RTM_NR_MSGTYPES: libc::c_ushort = (RTM_MAX + 1) - RTM_BASE; -pub const RTM_NR_FAMILIES: libc::c_ushort = RTM_NR_MSGTYPES >> 2; - -#[macro_export] -macro_rules! RTM_FAM { - ($cmd: expr) => { - ($cmd - RTM_BASE) >> 2 - }; -} - -#[repr(C)] -#[derive(Copy)] -pub struct rtattr { - rta_len: libc::c_ushort, - rta_type: libc::c_ushort, -} - -impl Clone for rtattr { - fn clone(&self) -> Self { - Self { ..*self } - } -} - -impl Default for rtattr { - fn default() -> Self { - Self { - ..unsafe { mem::zeroed::() } - } - } -} - -#[repr(C)] -#[derive(Copy)] -pub struct rtmsg { - rtm_family: libc::c_uchar, - rtm_dst_len: libc::c_uchar, - rtm_src_len: libc::c_uchar, - rtm_tos: libc::c_uchar, - rtm_table: libc::c_uchar, - rtm_protocol: libc::c_uchar, - rtm_scope: libc::c_uchar, - rtm_type: libc::c_uchar, - rtm_flags: libc::c_uint, -} - -impl Clone for rtmsg { - fn clone(&self) -> Self { - Self { ..*self } - } -} - -impl Default for rtmsg { - fn default() -> Self { - Self { - ..unsafe { mem::zeroed::() } - } - } -} - -// rtm_type c_uchar -pub const RTN_UNSPEC: libc::c_uchar = 0; -pub const RTN_UNICAST: libc::c_uchar = 1; -pub const RTN_LOCAL: libc::c_uchar = 2; -pub const RTN_BROADCAST: libc::c_uchar = 3; -pub const RTN_ANYCAST: libc::c_uchar = 4; -pub const RTN_MULTICAST: libc::c_uchar = 5; -pub const RTN_BLACKHOLE: libc::c_uchar = 6; -pub const RTN_UNREACHABLE: libc::c_uchar = 7; -pub const RTN_PROHIBIT: libc::c_uchar = 8; -pub const RTN_THROW: libc::c_uchar = 9; -pub const RTN_NAT: libc::c_uchar = 10; -pub const RTN_XRESOLVE: libc::c_uchar = 11; -pub const __RTN_MAX: libc::c_uchar = 12; -pub const RTN_MAX: libc::c_uchar = __RTN_MAX - 1; - -// rtm_protocol c_uchar -pub const RTPROTO_UNSPEC: libc::c_uchar = 0; -pub const RTPROTO_REDIRECT: libc::c_uchar = 1; -pub const RTPROTO_KERNEL: libc::c_uchar = 2; -pub const RTPROTO_BOOT: libc::c_uchar = 3; -pub const RTPROTO_STATIC: libc::c_uchar = 4; - -pub const RTPROTO_GATED: libc::c_uchar = 8; -pub const RTPROTO_RA: libc::c_uchar = 9; -pub const RTPROTO_MRT: libc::c_uchar = 10; -pub const RTPROTO_ZEBRA: libc::c_uchar = 11; -pub const RTPROTO_BIRD: libc::c_uchar = 12; -pub const RTPROTO_DNROUTED: libc::c_uchar = 13; -pub const RTPROTO_XORP: libc::c_uchar = 14; -pub const RTPROTO_NTK: libc::c_uchar = 15; -pub const RTPROTO_DHCP: libc::c_uchar = 16; -pub const RTPROTO_MROUTED: libc::c_uchar = 17; -pub const RTPROTO_BABEL: libc::c_uchar = 42; -pub const RTPROTO_BGP: libc::c_uchar = 186; -pub const RTPROTO_ISIS: libc::c_uchar = 187; -pub const RTPROTO_OSPF: libc::c_uchar = 188; -pub const RTPROTO_RIP: libc::c_uchar = 189; -pub const RTPROTO_EIGRP: libc::c_uchar = 192; - -//rtm_scope c_uchar -pub const RT_SCOPE_UNIVERSE: libc::c_uchar = 0; -pub const RT_SCOPE_SITE: libc::c_uchar = 200; -pub const RT_SCOPE_LINK: libc::c_uchar = 253; -pub const RT_SCOPE_HOST: libc::c_uchar = 254; -pub const RT_SCOPE_NOWHERE: libc::c_uchar = 255; - -// rtm_flags c_uint -pub const RTM_F_NOTIFY: libc::c_uint = 0x100; -pub const RTM_F_CLONED: libc::c_uint = 0x200; -pub const RTM_F_EQUALIZE: libc::c_uint = 0x400; -pub const RTM_F_PREFIX: libc::c_uint = 0x800; -pub const RTM_F_LOOKUP_TABLE: libc::c_uint = 0x1000; -pub const RTM_F_FIB_MATCH: libc::c_uint = 0x2000; - -// table identifier -pub const RT_TABLE_UNSPEC: libc::c_uint = 0; -pub const RT_TABLE_COMPAT: libc::c_uint = 252; -pub const RT_TABLE_DEFAULT: libc::c_uint = 253; -pub const RT_TABLE_MAIN: libc::c_uint = 254; -pub const RT_TABLE_LOCAL: libc::c_uint = 255; -pub const RT_TABLE_MAX: libc::c_uint = 0xffff_ffff; - -// rat_type c_ushort -pub const RTA_UNSPEC: libc::c_ushort = 0; -pub const RTA_DST: libc::c_ushort = 1; -pub const RTA_SRC: libc::c_ushort = 2; -pub const RTA_IIF: libc::c_ushort = 3; -pub const RTA_OIF: libc::c_ushort = 4; -pub const RTA_GATEWAY: libc::c_ushort = 5; -pub const RTA_PRIORITY: libc::c_ushort = 6; -pub const RTA_PREFSRC: libc::c_ushort = 7; -pub const RTA_METRICS: libc::c_ushort = 8; -pub const RTA_MULTIPATH: libc::c_ushort = 9; -pub const RTA_PROTOINFO: libc::c_ushort = 10; -pub const RTA_FLOW: libc::c_ushort = 11; -pub const RTA_CACHEINFO: libc::c_ushort = 12; -pub const RTA_SESSION: libc::c_ushort = 13; -pub const RTA_MP_ALGO: libc::c_ushort = 14; -pub const RTA_TABLE: libc::c_ushort = 15; -pub const RTA_MARK: libc::c_ushort = 16; -pub const RTA_MFC_STATS: libc::c_ushort = 17; -pub const RTA_VIA: libc::c_ushort = 18; -pub const RTA_NEWDST: libc::c_ushort = 19; -pub const RTA_PREF: libc::c_ushort = 20; -pub const RTA_ENCAP_TYPE: libc::c_ushort = 21; -pub const RTA_ENCAP: libc::c_ushort = 22; -pub const RTA_EXPIRES: libc::c_ushort = 23; -pub const RTA_PAD: libc::c_ushort = 24; -pub const RTA_UID: libc::c_ushort = 25; -pub const RTA_TTL_PROPAGATE: libc::c_ushort = 26; -pub const RTA_IP_PROTO: libc::c_ushort = 27; -pub const RTA_SPORT: libc::c_ushort = 28; -pub const RTA_DPORT: libc::c_ushort = 29; -pub const __RTA_MAX: libc::c_ushort = 30; -pub const RTA_MAX: libc::c_ushort = __RTA_MAX - 1; - -#[macro_export] -macro_rules! RTM_RTA { - ($rtm: expr) => {{ - let mut p = $rtm as *mut rtmsg as i64; - p += NLMSG_ALIGN!(mem::size_of::()) as i64; - p as *mut rtattr - }}; -} - -#[macro_export] -macro_rules! RTM_PAYLOAD { - ($h: expr) => { - NLMSG_PAYLOAD!($h, mem::size_of::()) - }; -} - -// RTA_MULTIPATH -#[repr(C)] -#[derive(Copy)] -pub struct rtnexthop { - rtnh_len: libc::c_ushort, - rtnh_flags: libc::c_uchar, - rtnh_hops: libc::c_uchar, - rtnh_ifindex: libc::c_int, -} - -impl Clone for rtnexthop { - fn clone(&self) -> Self { - Self { ..*self } - } -} - -impl Default for rtnexthop { - fn default() -> Self { - Self { - ..unsafe { mem::zeroed::() } - } - } -} - -// rtnh_flags -pub const RTNH_F_DEAD: libc::c_uchar = 1; -pub const RTNH_F_PERVASIVE: libc::c_uchar = 2; -pub const RTNH_F_ONLINK: libc::c_uchar = 4; -pub const RTNH_F_OFFLOAD: libc::c_uchar = 8; -pub const RTNH_F_LINKDOWN: libc::c_uchar = 16; -pub const RTNH_F_UNRESOLVED: libc::c_uchar = 32; - -pub const RTNH_COMPARE_MASK: libc::c_uchar = RTNH_F_DEAD | RTNH_F_LINKDOWN | RTNH_F_OFFLOAD; - -pub const RTNH_ALIGN: i32 = 4; -#[macro_export] -macro_rules! RTNH_ALIGN { - ($len: expr) => { - (($len as u32 + (RTNH_ALIGN - 1) as u32) & !(RTNH_ALIGN - 1) as u32) - }; -} - -#[macro_export] -macro_rules! RTNH_OK { - ($rtnh: expr, $len: expr) => { - $rtnh.rtnh_len >= mem::size_of::() && $rtnh.rtnh_len <= $len - }; -} - -#[macro_export] -macro_rules! RTNH_NEXT { - ($rtnh: expr) => { - unsafe { - let mut p = $rtnh as *mut rtnexthop as i64; - p += RTNH_ALIGN!($rtnh.rtnh_len); - p as *mut rtnexthop - } - }; -} - -#[macro_export] -macro_rules! RTNH_LENGTH { - ($len: expr) => { - RTNH_ALIGN!(mem::size_of::()) + $len - }; -} - -#[macro_export] -macro_rules! RTNH_SPACE { - ($len: expr) => { - RTNH_ALIGN!(RTNH_LENGTH!($len)) - }; -} - -#[macro_export] -macro_rules! RTNH_DATA { - ($rtnh: expr) => {{ - let mut p = $rtnh as *mut rtnexthop as i64; - p += RTNH_LENGTH!(0); - p as *mut rtattr - }}; -} - -// RTA_VIA -type __kernel_sa_family_t = libc::c_ushort; -#[repr(C)] -#[derive(Copy)] -pub struct rtvia { - rtvia_family: __kernel_sa_family_t, - // array with size 0. omitted here. be careful - // with how to access it. cannot use rtvia.rtvia_addr -} - -impl Clone for rtvia { - fn clone(&self) -> Self { - Self { ..*self } - } -} - -impl Default for rtvia { - fn default() -> Self { - Self { - ..unsafe { mem::zeroed::() } - } - } -} - -// rta_cacheinfo -#[repr(C)] -#[derive(Copy)] -pub struct rta_cacheinfo { - rta_clntref: __u32, - rta_lastuse: __u32, - rta_expires: __u32, - rta_error: __u32, - rta_used: __u32, - - rta_id: __u32, - rta_ts: __u32, - rta_tsage: __u32, -} - -impl Clone for rta_cacheinfo { - fn clone(&self) -> Self { - Self { ..*self } - } -} - -impl Default for rta_cacheinfo { - fn default() -> Self { - Self { - ..unsafe { mem::zeroed::() } - } - } -} - -// RTA_METRICS -pub const RTAX_UNSPEC: libc::c_ushort = 0; -pub const RTAX_LOCK: libc::c_ushort = 1; -pub const RTAX_MTU: libc::c_ushort = 2; -pub const RTAX_WINDOW: libc::c_ushort = 3; -pub const RTAX_RTT: libc::c_ushort = 4; -pub const RTAX_RTTVAR: libc::c_ushort = 5; -pub const RTAX_SSTHRESH: libc::c_ushort = 6; -pub const RTAX_CWND: libc::c_ushort = 7; -pub const RTAX_ADVMSS: libc::c_ushort = 8; -pub const RTAX_REORDERING: libc::c_ushort = 9; -pub const RTAX_HOPLIMIT: libc::c_ushort = 10; -pub const RTAX_INITCWND: libc::c_ushort = 11; -pub const RTAX_FEATURES: libc::c_ushort = 12; -pub const RTAX_RTO_MIN: libc::c_ushort = 13; -pub const RTAX_INITRWND: libc::c_ushort = 14; -pub const RTAX_QUICKACK: libc::c_ushort = 15; -pub const RTAX_CC_ALGO: libc::c_ushort = 16; -pub const RTAX_FASTOPEN_NO_COOKIE: libc::c_ushort = 17; -pub const __RTAX_MAX: libc::c_ushort = 18; - -pub const RTAX_MAX: libc::c_ushort = __RTAX_MAX - 1; -pub const RTAX_FEATURE_ECN: libc::c_ushort = 0x1; -pub const RTAX_FEATURE_SACK: libc::c_ushort = 0x2; -pub const RTAX_FEATURE_TIMESTAMP: libc::c_ushort = 0x4; -pub const RTAX_FEATURE_ALLFRAG: libc::c_ushort = 0x8; -pub const RTAX_FEATURE_MASK: libc::c_ushort = - RTAX_FEATURE_ECN | RTAX_FEATURE_SACK | RTAX_FEATURE_TIMESTAMP | RTAX_FEATURE_ALLFRAG; - -// RTA_SESSION -#[repr(C)] -#[derive(Copy)] -pub struct Ports { - sport: __u16, - dport: __u16, -} - -impl Clone for Ports { - fn clone(&self) -> Self { - Self { ..*self } - } -} - -impl Default for Ports { - fn default() -> Self { - Self { - ..unsafe { mem::zeroed::() } - } - } -} - -#[repr(C)] -#[derive(Copy)] -pub struct Icmpt { - r#type: __u8, - code: __u8, - ident: __u16, -} - -impl Clone for Icmpt { - fn clone(&self) -> Self { - Self { ..*self } - } -} - -impl Default for Icmpt { - fn default() -> Self { - Self { - ..unsafe { mem::zeroed::() } - } - } -} - -#[repr(C)] -#[derive(Copy)] -pub union U { - pub ports: Ports, - pub icmpt: Icmpt, - spi: __u32, -} - -impl Clone for U { - fn clone(&self) -> Self { - Self { - spi: unsafe { self.spi }, - } - } -} - -impl Default for U { - fn default() -> Self { - let s = unsafe { mem::zeroed::() }; - Self { - spi: unsafe { s.spi }, - } - } -} - -#[repr(C)] -#[derive(Copy)] -pub struct rta_session { - proto: __u8, - pad1: __u8, - pad2: __u16, - u: U, -} - -impl Clone for rta_session { - fn clone(&self) -> Self { - Self { ..*self } - } -} - -impl Default for rta_session { - fn default() -> Self { - Self { - ..unsafe { mem::zeroed::() } - } - } -} - -#[repr(C)] -#[derive(Copy)] -pub struct rta_mfc_stats { - mfcs_packets: __u64, - mfcs_bytes: __u64, - mfcs_wrong_if: __u64, -} - -impl Clone for rta_mfc_stats { - fn clone(&self) -> Self { - Self { ..*self } - } -} - -impl Default for rta_mfc_stats { - fn default() -> Self { - Self { - ..unsafe { mem::zeroed::() } - } - } -} - -#[repr(C)] -#[derive(Copy)] -pub struct ifinfomsg { - ifi_family: libc::c_uchar, - __ifi_pad: libc::c_uchar, - ifi_type: libc::c_ushort, - ifi_index: libc::c_int, - ifi_flags: libc::c_uint, - ifi_change: libc::c_uint, -} - -impl Clone for ifinfomsg { - fn clone(&self) -> Self { - Self { ..*self } - } -} - -impl Default for ifinfomsg { - fn default() -> Self { - Self { - ..unsafe { mem::zeroed::() } - } - } -} - -#[repr(C)] -#[derive(Copy)] -pub struct rtnl_link_stats64 { - rx_packets: __u64, - tx_packets: __u64, - rx_bytes: __u64, - tx_bytes: __u64, - rx_errors: __u64, - tx_errors: __u64, - rx_dropped: __u64, - tx_dropped: __u64, - multicast: __u64, - collisions: __u64, - - // detailed rx_errors - rx_length_errors: __u64, - rx_over_errors: __u64, - rx_crc_errors: __u64, - rx_frame_errrors: __u64, - rx_fifo_errors: __u64, - rx_missed_errors: __u64, - - // detailed tx_errors - tx_aborted_errors: __u64, - tx_carrier_errors: __u64, - tx_fifo_errors: __u64, - tx_heartbeat_errors: __u64, - tx_window_errors: __u64, - - rx_compressed: __u64, - tx_compressed: __u64, - rx_nohandler: __u64, -} - -impl Clone for rtnl_link_stats64 { - fn clone(&self) -> Self { - Self { ..*self } - } -} - -impl Default for rtnl_link_stats64 { - fn default() -> Self { - Self { - ..unsafe { mem::zeroed::() } - } - } -} - -#[repr(C)] -#[derive(Copy)] -pub struct rtnl_link_stats { - rx_packets: __u32, - tx_packets: __u32, - rx_bytes: __u32, - tx_bytes: __u32, - rx_errors: __u32, - tx_errors: __u32, - rx_dropped: __u32, - tx_dropped: __u32, - multicast: __u32, - collisions: __u32, - - // detailed rx_errors - rx_length_errors: __u32, - rx_over_errors: __u32, - rx_crc_errors: __u32, - rx_frame_errrors: __u32, - rx_fifo_errors: __u32, - rx_missed_errors: __u32, - - // detailed tx_errors - tx_aborted_errors: __u32, - tx_carrier_errors: __u32, - tx_fifo_errors: __u32, - tx_heartbeat_errors: __u32, - tx_window_errors: __u32, - - rx_compressed: __u32, - tx_compressed: __u32, - rx_nohandler: __u32, -} - -impl Clone for rtnl_link_stats { - fn clone(&self) -> Self { - Self { ..*self } - } -} - -impl Default for rtnl_link_stats { - fn default() -> Self { - Self { - ..unsafe { mem::zeroed::() } - } - } -} - -#[repr(C)] -#[derive(Copy)] -pub struct ifaddrmsg { - ifa_family: __u8, - ifa_prefixlen: __u8, - ifa_flags: __u8, - ifa_scope: __u8, - ifa_index: __u32, -} - -impl Clone for ifaddrmsg { - fn clone(&self) -> Self { - Self { ..*self } - } -} - -impl Default for ifaddrmsg { - fn default() -> Self { - Self { - ..unsafe { mem::zeroed::() } - } - } -} - -#[repr(C)] -#[derive(Copy)] -pub struct ifa_cacheinfo { - ifa_prefered: __u32, - ifa_valid: __u32, - cstamp: __u32, - tstamp: __u32, -} - -impl Clone for ifa_cacheinfo { - fn clone(&self) -> Self { - Self { ..*self } - } -} - -impl Default for ifa_cacheinfo { - fn default() -> Self { - Self { - ..unsafe { mem::zeroed::() } - } - } -} - -pub const RTA_ALIGNTO: libc::c_uint = 4; -#[macro_export] -macro_rules! RTA_ALIGN { - ($x: expr) => { - ($x as u32 + (RTA_ALIGNTO - 1) as u32) & !((RTA_ALIGNTO - 1) as u32) - }; -} - -#[macro_export] -macro_rules! RTA_OK { - ($attr: expr, $len: expr) => { - ($len as u32 >= mem::size_of::() as u32) - && ((*$attr).rta_len as u32 >= mem::size_of::() as u32) - && ((*$attr).rta_len as u32 <= $len as u32) - }; -} - -#[macro_export] -macro_rules! RTA_NEXT { - ($attr: expr, $len: expr) => {{ - $len -= RTA_ALIGN!((*$attr).rta_len) as u32; - let mut p = $attr as *mut libc::c_char as i64; - p += RTA_ALIGN!((*$attr).rta_len) as i64; - p as *mut rtattr - }}; -} - -#[macro_export] -macro_rules! RTA_LENGTH { - ($len: expr) => { - RTA_ALIGN!($len as u32 + mem::size_of::() as u32) - }; -} - -#[macro_export] -macro_rules! RTA_SPACE { - ($len: expr) => { - RTA_ALIGN!(RTA_LENGTH!($len)) - }; -} - -#[macro_export] -macro_rules! RTA_DATA { - ($attr: expr) => {{ - let mut p = $attr as *mut libc::c_char as i64; - p += RTA_LENGTH!(0) as i64; - p as *mut libc::c_char - }}; -} - -#[macro_export] -macro_rules! RTA_PAYLOAD { - ($attr: expr) => { - ((*$attr).rta_len as u32 - RTA_LENGTH!(0) as u32) - }; -} - -pub const NLMSGERR_ATTR_UNUSED: libc::c_uchar = 0; -pub const NLMSGERR_ATTR_MASG: libc::c_uchar = 1; -pub const NLMSGERR_ATTR_OFFS: libc::c_uchar = 2; -pub const NLMSGERR_ATTR_COOKIE: libc::c_uchar = 3; -pub const __NLMSGERR_ATTR_MAX: libc::c_uchar = 4; -pub const NLMSGERR_ATTR_MAX: libc::c_uchar = __NLMSGERR_ATTR_MAX - 1; - -pub const NLMSG_ALIGNTO: libc::c_uint = 4; -#[macro_export] -macro_rules! NLMSG_ALIGN { - ($len: expr) => { - ($len as u32 + NLMSG_ALIGNTO - 1) & !(NLMSG_ALIGNTO - 1) - }; -} - -// weird, static link cannot find libc::nlmsghdr -// define macro here ro work around it for now -// till someone can find out the reason -// pub const NLMSG_HDRLEN: libc::c_int = NLMSG_ALIGN!(mem::size_of::() as libc::c_uint) as libc::c_int; - -#[macro_export] -macro_rules! NLMSG_HDRLEN { - () => { - NLMSG_ALIGN!(mem::size_of::()) - }; -} - -#[macro_export] -macro_rules! NLMSG_LENGTH { - ($len: expr) => { - ($len as u32 + NLMSG_HDRLEN!()) - }; -} - -#[macro_export] -macro_rules! NLMSG_SPACE { - ($len: expr) => { - NLMSG_ALIGN!(NLMSG_LENGTH!($len)) - }; -} - -#[macro_export] -macro_rules! NLMSG_DATA { - ($nlh: expr) => {{ - let mut p = $nlh as *const nlmsghdr as i64; - p += NLMSG_LENGTH!(0) as i64; - p as *mut libc::c_void - }}; -} - -#[macro_export] -macro_rules! NLMSG_NEXT { - ($nlh: expr, $len: expr) => {{ - $len -= NLMSG_ALIGN!($nlh.nlmsg_len) as u32; - let mut p = $nlh as *const nlmsghdr as *mut libc::c_char; - p = (p as i64 + NLMSG_ALIGN!($nlh.nlmsg_len) as i64) as *mut libc::c_char; - unsafe { &mut *(p as *mut nlmsghdr) } - }}; -} - -#[macro_export] -macro_rules! NLMSG_OK { - ($nlh: expr, $len: expr) => { - $len as usize >= mem::size_of::() - && (*$nlh).nlmsg_len as usize >= mem::size_of::() - && (*$nlh).nlmsg_len as usize <= $len as usize - }; -} - -#[macro_export] -macro_rules! NLMSG_PAYLOAD { - ($nlh: expr, $len: expr) => { - ((*$nlh).nlmsg_len - NLMSG_SPACE!($len)) - }; -} - -#[macro_export] -macro_rules! RTA_TAIL { - ($attr: expr) => { - unsafe { - let mut p = $attr as *mut rtattr as i64; - p += RTA_ALIGN!($attr->rta_len) as i64; - p as *mut rtattr - } - } -} - -#[macro_export] -macro_rules! NLMSG_TAIL { - ($msg: expr) => {{ - let mut p = $msg as *mut nlmsghdr as i64; - p += NLMSG_ALIGN!((*$msg).nlmsg_len) as i64; - p as *mut rtattr - }}; -} - -#[macro_export] -macro_rules! IFA_RTA { - ($ifmsg: expr) => {{ - let mut p = $ifmsg as *const ifaddrmsg as *const libc::c_char; - p = (p as i64 + NLMSG_ALIGN!(mem::size_of::()) as i64) as *mut libc::c_char; - p as *const rtattr - }}; -} - -#[macro_export] -macro_rules! IFA_PAYLOAD { - ($h: expr) => { - NLMSG_PAYLOAD!($h, mem::size_of::()) - }; -} - -#[macro_export] -macro_rules! IFLA_RTA { - ($ifinfo: expr) => {{ - let mut p = $ifinfo as *mut ifinfomsg as i64; - p += NLMSG_ALIGN!(mem::size_of::()) as i64; - p as *mut rtattr - }}; -} - -#[macro_export] -macro_rules! IFLA_PAYLOAD { - ($h: expr) => { - (NLMSG_PAYLOAD!($h, mem::size_of::())) - }; -} - -#[macro_export] -macro_rules! IFLA_STATS_RTA { - ($stats: expr) => {{ - let mut p = $stats as *mut if_stats_msg as i64; - p += NLMSG_ALIGN!(mem::size_of::()) as i64; - p as *mut rtattr - }}; -} - -#[repr(C)] -#[derive(Copy)] -pub struct nlmsghdr { - pub nlmsg_len: __u32, - pub nlmsg_type: __u16, - pub nlmsg_flags: __u16, - pub nlmsg_seq: __u32, - pub nlmsg_pid: __u32, -} - -impl Clone for nlmsghdr { - fn clone(&self) -> Self { - Self { ..*self } - } -} - -impl Default for nlmsghdr { - fn default() -> Self { - unsafe { mem::zeroed::() } - } -} - -impl Borrow for Vec { - fn borrow(&self) -> &nlmsghdr { - let ptr = self.as_ptr(); - assert_eq!(ptr.align_offset(std::mem::align_of::()), 0); - unsafe { &*(ptr as *const nlmsghdr) } - } -} - -// nlmsg_flags -pub const NLM_F_REQUEST: __u16 = 0x01; -pub const NLM_F_MULTI: __u16 = 0x02; -pub const NLM_F_ACK: __u16 = 0x04; -pub const NLM_F_ECHO: __u16 = 0x08; -pub const NLM_F_DUMP_INTR: __u16 = 0x10; -pub const NLM_F_DUMP_FILTERED: __u16 = 0x20; - -// Get Request -pub const NLM_F_ROOT: __u16 = 0x100; -pub const NLM_F_MATCH: __u16 = 0x200; -pub const NLM_F_ATOMIC: __u16 = 0x400; -pub const NLM_F_DUMP: __u16 = NLM_F_ROOT | NLM_F_MATCH; - -// New Request -pub const NLM_F_REPLACE: __u16 = 0x100; -pub const NLM_F_EXCL: __u16 = 0x200; -pub const NLM_F_CREATE: __u16 = 0x400; -pub const NLM_F_APPEND: __u16 = 0x800; - -// Delete Request -pub const NLM_F_NONREC: __u16 = 0x100; - -//ACK message -pub const NLM_F_CAPPED: __u16 = 0x100; -pub const NLM_F_ACK_TLVS: __u16 = 0x200; - -// error message type -pub const NLMSG_NOOP: __u16 = 0x1; -pub const NLMSG_ERROR: __u16 = 0x2; -pub const NLMSG_DONE: __u16 = 0x3; -pub const NLMSG_OVERRUN: __u16 = 0x4; - -pub const NLMSG_MIN_TYPE: __u16 = 0x10; - -// IFLA_EXT_MASK -pub const RTEXT_FILTER_VF: __u32 = 0x1; -pub const RTEXT_FILTER_BRVLAN: __u32 = 0x2; -pub const RTEXT_FILTER_BRVLAN_COMPRESSED: __u32 = 0x4; -pub const RTEXT_FILTER_SKIP_STATS: __u32 = 0x8; - -// IFLA attr -pub const IFLA_UNSPEC: __u16 = 0; -pub const IFLA_ADDRESS: __u16 = 1; -pub const IFLA_BROADCAST: __u16 = 2; -pub const IFLA_IFNAME: __u16 = 3; -pub const IFLA_MTU: __u16 = 4; -pub const IFLA_LINK: __u16 = 5; -pub const IFLA_QDISC: __u16 = 6; -pub const IFLA_STATS: __u16 = 7; -pub const IFLA_COST: __u16 = 8; -pub const IFLA_PRIORITY: __u16 = 9; -pub const IFLA_MASTER: __u16 = 10; -pub const IFLA_WIRELESS: __u16 = 11; -pub const IFLA_PROTINFO: __u16 = 12; -pub const IFLA_TXQLEN: __u16 = 13; -pub const IFLA_MAP: __u16 = 14; -pub const IFLA_WEIGHT: __u16 = 15; -pub const IFLA_OPERSTATE: __u16 = 16; -pub const IFLA_LINKMODE: __u16 = 17; -pub const IFLA_LINKINFO: __u16 = 18; -pub const IFLA_NET_NS_PID: __u16 = 19; -pub const IFLA_IFALIAS: __u16 = 20; -pub const IFLA_NUM_VF: __u16 = 21; -pub const IFLA_VFINFO_LIST: __u16 = 22; -pub const IFLA_STATS64: __u16 = 23; -pub const IFLA_VF_PORTS: __u16 = 24; -pub const IFLA_PORT_SELF: __u16 = 25; -pub const IFLA_AF_SPEC: __u16 = 26; -pub const IFLA_GROUP: __u16 = 27; -pub const IFLA_NET_NS_FD: __u16 = 28; -pub const IFLA_EXT_MASK: __u16 = 29; -pub const IFLA_PROMISCUITY: __u16 = 30; -pub const IFLA_NUM_TX_QUEUES: __u16 = 31; -pub const IFLA_NUM_RX_QUEUES: __u16 = 32; -pub const IFLA_CARRIER: __u16 = 33; -pub const IFLA_PHYS_PORT_ID: __u16 = 34; -pub const IFLA_CARRIER_CHANGES: __u16 = 35; -pub const IFLA_PHYS_SWITCH_ID: __u16 = 36; -pub const IFLA_LINK_NETNSID: __u16 = 37; -pub const IFLA_PHYS_PORT_NAME: __u16 = 38; -pub const IFLA_PROTO_DOWN: __u16 = 39; -pub const IFLA_GSO_MAX_SEGS: __u16 = 40; -pub const IFLA_GSO_MAX_SIZE: __u16 = 41; -pub const IFLA_PAD: __u16 = 42; -pub const IFLA_XDP: __u16 = 43; -pub const IFLA_EVENT: __u16 = 44; -pub const IFLA_NEW_NETNSID: __u16 = 45; -pub const IFLA_IF_NETNSID: __u16 = 46; -pub const IFLA_CARRIER_UP_COUNT: __u16 = 47; -pub const IFLA_CARRIER_DOWN_COUNT: __u16 = 48; -pub const IFLA_NEW_IFINDEX: __u16 = 49; -pub const IFLA_MIN_MTU: __u16 = 50; -pub const IFLA_MAX_MTU: __u16 = 51; -pub const __IFLA_MAX: __u16 = 52; -pub const IFLA_MAX: __u16 = __IFLA_MAX - 1; - -pub const IFA_UNSPEC: __u16 = 0; -pub const IFA_ADDRESS: __u16 = 1; -pub const IFA_LOCAL: __u16 = 2; -pub const IFA_LABEL: __u16 = 3; -pub const IFA_BROADCAST: __u16 = 4; -pub const IFA_ANYCAST: __u16 = 5; -pub const IFA_CACHEINFO: __u16 = 6; -pub const IFA_MULTICAST: __u16 = 7; -pub const IFA_FLAGS: __u16 = 8; -pub const IFA_RT_PRIORITY: __u16 = 9; -pub const __IFA_MAX: __u16 = 10; -pub const IFA_MAX: __u16 = __IFA_MAX - 1; - -// ifa_flags -pub const IFA_F_SECONDARY: __u32 = 0x01; -pub const IFA_F_TEMPORARY: __u32 = IFA_F_SECONDARY; -pub const IFA_F_NODAD: __u32 = 0x02; -pub const IFA_F_OPTIMISTIC: __u32 = 0x04; -pub const IFA_F_DADFAILED: __u32 = 0x08; -pub const IFA_F_HOMEADDRESS: __u32 = 0x10; -pub const IFA_F_DEPRECATED: __u32 = 0x20; -pub const IFA_F_TENTATIVE: __u32 = 0x40; -pub const IFA_F_PERMANENT: __u32 = 0x80; -pub const IFA_F_MANAGETEMPADDR: __u32 = 0x100; -pub const IFA_F_NOPREFIXROUTE: __u32 = 0x200; -pub const IFA_F_MCAUTOJOIN: __u32 = 0x400; -pub const IFA_F_STABLE_PRIVACY: __u32 = 0x800; - -#[repr(C)] -#[derive(Copy)] -pub struct ndmsg { - ndm_family: __u8, - ndm_pad1: __u8, - ndm_pad: __u16, - ndm_ifindex: __s32, - ndm_state: __u16, - ndm_flags: __u8, - ndm_type: __u8, -} - -pub const NDA_UNSPEC: __u16 = 0; -pub const NDA_DST: __u16 = 1; -pub const NDA_LLADDR: __u16 = 2; -pub const NDA_CACHEINFO: __u16 = 3; -pub const NDA_PROBES: __u16 = 4; -pub const NDA_VLAN: __u16 = 5; -pub const NDA_PORT: __u16 = 6; -pub const NDA_VNI: __u16 = 7; -pub const NDA_IFINDEX: __u16 = 8; -pub const NDA_MASTER: __u16 = 9; -pub const NDA_LINK_NETNSID: __u16 = 10; -pub const NDA_SRC_VNI: __u16 = 11; -pub const __NDA_MAX: __u16 = 12; - -impl Clone for ndmsg { - fn clone(&self) -> Self { - Self { ..*self } - } -} - -impl Default for ndmsg { - fn default() -> Self { - unsafe { mem::zeroed::() } - } -} - -#[repr(C)] -#[derive(Copy)] -pub struct nlmsgerr { - pub error: libc::c_int, - pub msg: nlmsghdr, -} - -impl Clone for nlmsgerr { - fn clone(&self) -> Self { - Self { ..*self } - } -} - -impl Default for nlmsgerr { - fn default() -> Self { - unsafe { mem::zeroed::() } - } -} - -pub const NETLINK_ROUTE: libc::c_int = 0; -pub const NETLINK_EXT_ACK: libc::c_int = 11; -pub const NETLINK_UEVENT: libc::c_int = 15; - -pub struct RtRoute { - pub dest: Option>, - pub source: Option>, - pub gateway: Option>, - pub index: i32, - pub scope: u8, - pub dst_len: u8, - pub src_len: u8, - pub protocol: u8, -} - -impl Default for RtRoute { - fn default() -> Self { - unsafe { mem::zeroed::() } - } -} - -pub struct RtIPAddr { - pub ip_family: __u8, - pub ip_mask: __u8, - pub addr: Vec, -} - -/// Handle to access the Linux Netlink subsystem. -// #[derive(Copy)] -pub struct RtnlHandle { - pub fd: RawFd, - local: libc::sockaddr_nl, - seq: __u32, - dump: __u32, -} - -impl Clone for RtnlHandle { - fn clone(&self) -> Self { - Self { ..*self } - } -} - -impl Default for RtnlHandle { - fn default() -> Self { - Self { - ..unsafe { mem::zeroed::() } - } - } -} - -impl fmt::Debug for RtnlHandle { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!( - f, - "fd: {}\nadrr:{{pid: {}, family: {}}}\nseq:{}\ndump:{}", - self.fd, self.local.nl_family, self.local.nl_pid, self.seq, self.dump - ) - } -} - -impl RtnlHandle { - pub fn new(protocal: libc::c_int, group: u32) -> Result { - // open netlink_route socket - let mut sa: libc::sockaddr_nl = unsafe { mem::zeroed::() }; - let fd = unsafe { - let tmpfd = libc::socket( - libc::AF_NETLINK, - libc::SOCK_DGRAM | libc::SOCK_CLOEXEC, - protocal, - ); - - let sndbuf: libc::c_int = 32768; - let rcvbuf: libc::c_int = 1024 * 1024; - let one: libc::c_int = 1; - let mut addrlen: libc::socklen_t = - mem::size_of::() as libc::socklen_t; - - if tmpfd < 0 { - return nix_last_os_err(); - } - - let mut err = libc::setsockopt( - tmpfd, - libc::SOL_SOCKET, - libc::SO_SNDBUF, - &sndbuf as *const libc::c_int as *const libc::c_void, - mem::size_of::() as libc::socklen_t, - ); - - if err < 0 { - libc::close(tmpfd); - return nix_last_os_err(); - } - - err = libc::setsockopt( - tmpfd, - libc::SOL_SOCKET, - libc::SO_RCVBUF, - &rcvbuf as *const libc::c_int as *const libc::c_void, - mem::size_of::() as libc::socklen_t, - ); - - if err < 0 { - libc::close(tmpfd); - return nix_last_os_err(); - } - - libc::setsockopt( - tmpfd, - libc::SOL_NETLINK, - NETLINK_EXT_ACK, - &one as *const libc::c_int as *const libc::c_void, - mem::size_of::() as libc::socklen_t, - ); - - sa.nl_family = libc::AF_NETLINK as __u16; - sa.nl_groups = group; - - err = libc::bind( - tmpfd, - (&sa as *const libc::sockaddr_nl) as *const libc::sockaddr, - mem::size_of::() as libc::socklen_t, - ); - if err < 0 { - libc::close(tmpfd); - return nix_last_os_err(); - } - - err = libc::getsockname( - tmpfd, - &mut sa as *mut libc::sockaddr_nl as *mut libc::sockaddr, - &mut addrlen as *mut libc::socklen_t, - ); - if err < 0 { - libc::close(tmpfd); - return nix_last_os_err(); - } - - if sa.nl_family as i32 != libc::AF_NETLINK - || addrlen as usize != mem::size_of::() - { - libc::close(tmpfd); - return nix_errno(Errno::EINVAL); - } - - tmpfd - }; - - Ok(Self { - fd, - local: sa, - seq: unsafe { libc::time(std::ptr::null_mut::()) } as __u32, - dump: 0, - }) - } - - pub fn rtnl_talk(&mut self, data: &mut [u8], answer: bool) -> Result> { - if data.len() < std::mem::size_of::() { - return nix_errno(Errno::EINVAL); - } - // Safe because we have just validated buffer size above. - let nlh = unsafe { &mut *(data.as_mut_ptr() as *mut nlmsghdr) }; - if !answer { - nlh.nlmsg_flags |= NLM_F_ACK; - } - self.send_message(data)?; - - loop { - let buf = self.recv_message()?; - let mut msglen = buf.len() as u32; - if (msglen as usize) < std::mem::size_of::() { - return nix_errno(Errno::EBADMSG); - } - // Safe because we have just validated buffer size above. - let mut nlh = unsafe { &*(buf.as_ptr() as *const nlmsghdr) }; - - while NLMSG_OK!(nlh, msglen) { - if nlh.nlmsg_pid != self.local.nl_pid { - nlh = NLMSG_NEXT!(nlh, msglen); - continue; - } - - if nlh.nlmsg_type == NLMSG_ERROR { - if nlh.nlmsg_len < NLMSG_LENGTH!(mem::size_of::()) { - // truncated - return nix_errno(Errno::EBADMSG); - } - - // Safe because we have just validated buffer size above. - let el = unsafe { &*(NLMSG_DATA!(nlh) as *const nlmsgerr) }; - - // this is ack. -_- - if el.error == 0 { - return Ok(Vec::new()); - } - - return nix_errno(Errno::from_i32(-el.error)); - } - - // good message - let mut result = Vec::new(); - if answer { - // need to copy out data - result.resize(nlh.nlmsg_len as usize, 0); - let dp: *mut libc::c_void = result.as_mut_ptr() as *mut libc::c_void; - // Safe because the source and destination buffers are valid. - unsafe { - libc::memcpy( - dp, - nlh as *const nlmsghdr as *const libc::c_void, - nlh.nlmsg_len as libc::size_t, - ) - }; - }; - - return Ok(result); - } - - if !(NLMSG_OK!(nlh, msglen)) { - return nix_errno(Errno::EINVAL); - } - } - } - - pub fn send_message(&self, data: &mut [u8]) -> Result<()> { - if data.len() < std::mem::size_of::() { - return nix_errno(Errno::EINVAL); - } - - let mut sa: libc::sockaddr_nl = unsafe { mem::zeroed::() }; - let mut h = unsafe { mem::zeroed::() }; - - // Safe because we have validated the data buffer size is bigger then nlmsghdr. - let nh = unsafe { &mut *(data.as_mut_ptr() as *mut nlmsghdr) }; - if nh.nlmsg_len as usize > data.len() { - return nix_errno(Errno::EINVAL); - } - let mut iov: libc::iovec = libc::iovec { - iov_base: nh as *mut nlmsghdr as *mut libc::c_void, - iov_len: nh.nlmsg_len as libc::size_t, - }; - - h.msg_name = &mut sa as *mut libc::sockaddr_nl as *mut libc::c_void; - h.msg_namelen = mem::size_of::() as libc::socklen_t; - h.msg_iov = &mut iov as *mut libc::iovec; - h.msg_iovlen = 1; - - sa.nl_family = libc::AF_NETLINK as u16; - - let err = unsafe { libc::sendmsg(self.fd, &h as *const libc::msghdr, 0) }; - if err < 0 { - return nix_last_os_err(); - } - - Ok(()) - } - - pub fn recv_message(&self) -> Result> { - let mut sa: libc::sockaddr_nl = unsafe { mem::zeroed::() }; - let mut h = unsafe { mem::zeroed::() }; - let mut iov = libc::iovec { - iov_base: std::ptr::null_mut::(), - iov_len: 0 as libc::size_t, - }; - - h.msg_name = &mut sa as *mut libc::sockaddr_nl as *mut libc::c_void; - h.msg_namelen = mem::size_of::() as libc::socklen_t; - h.msg_iov = &mut iov as *mut libc::iovec; - h.msg_iovlen = 1; - - let mut rlen = unsafe { - libc::recvmsg( - self.fd, - &mut h as *mut libc::msghdr, - libc::MSG_PEEK | libc::MSG_TRUNC, - ) - }; - - if rlen < 0 { - return nix_last_os_err(); - } - - let mut v: Vec = vec![0; rlen as usize]; - iov.iov_base = v.as_mut_ptr() as *mut libc::c_void; - iov.iov_len = rlen as libc::size_t; - - rlen = unsafe { libc::recvmsg(self.fd, &mut h as *mut libc::msghdr, 0) }; - if rlen < 0 { - return nix_last_os_err(); - } - - if sa.nl_pid != 0 { - // not our netlink message - return nix_errno(Errno::EBADMSG); - } - - if h.msg_flags & libc::MSG_TRUNC != 0 { - return nix_errno(Errno::EBADMSG); - } - - v.resize(rlen as usize, 0); - - Ok(v) - } - - fn recv_dump_message(&self) -> Result<(Vec>, Vec<*const nlmsghdr>)> { - let mut slv: Vec> = Vec::new(); - let mut lv: Vec<*const nlmsghdr> = Vec::new(); - - loop { - let buf = self.recv_message()?; - - let mut msglen = buf.len() as u32; - let mut nlh: &nlmsghdr = buf.borrow(); - let mut dump_intr = false; - let mut done = false; - - while NLMSG_OK!(nlh, msglen) { - // Make sure we are interested in the message first. - if nlh.nlmsg_pid != self.local.nl_pid || nlh.nlmsg_seq != self.dump { - nlh = NLMSG_NEXT!(nlh, msglen); - continue; - } - - if nlh.nlmsg_flags & NLM_F_DUMP_INTR > 0 { - dump_intr = true; - } - - if nlh.nlmsg_type == NLMSG_DONE { - done = true; - } - - if nlh.nlmsg_type == NLMSG_ERROR { - // error message, better to return error code in error messages - if nlh.nlmsg_len < NLMSG_LENGTH!(mem::size_of::()) { - // truncated - return nix_errno(Errno::EBADMSG); - } - - // Safe because we have validated buffer size. - let el = unsafe { &*(NLMSG_DATA!(nlh) as *const nlmsgerr) }; - return nix_errno(Errno::from_i32(-el.error)); - } - - lv.push(nlh); - - if done { - break; - } - - nlh = NLMSG_NEXT!(nlh, msglen); - } - - slv.push(buf); - - if done { - if dump_intr { - info!(sl!(), "dump interrupted, maybe incomplete"); - } - - break; - } - - // still remain some bytes? - if msglen != 0 { - return nix_errno(Errno::EINVAL); - } - } - - Ok((slv, lv)) - } - - fn dump_all_links(&mut self) -> Result<(Vec>, Vec<*const nlmsghdr>)> { - let mut v: Vec = vec![0; DEFAULT_NETLINK_BUF_SIZE]; - // Safe because we have allocated enough buffer space. - let nlh = unsafe { &mut *(v.as_mut_ptr() as *mut nlmsghdr) }; - let ifi = unsafe { &mut *(NLMSG_DATA!(nlh) as *mut ifinfomsg) }; - - nlh.nlmsg_len = NLMSG_LENGTH!(mem::size_of::() as i32) as __u32; - nlh.nlmsg_type = RTM_GETLINK; - nlh.nlmsg_flags = (NLM_F_DUMP | NLM_F_REQUEST) as __u16; - self.assign_seqnum(nlh); - - ifi.ifi_family = libc::AF_UNSPEC as u8; - - // Safe because we have allocated enough buffer space. - unsafe { - nlh.addattr32(IFLA_EXT_MASK, RTEXT_FILTER_VF); - } - - self.send_message(v.as_mut_slice())?; - self.recv_dump_message() - } - - fn dump_all_addresses( - &mut self, - ifindex: __u32, - ) -> Result<(Vec>, Vec<*const nlmsghdr>)> { - let mut v: Vec = vec![0; 2048]; - // Safe because we have allocated enough buffer space. - let nlh = unsafe { &mut *(v.as_mut_ptr() as *mut nlmsghdr) }; - let ifa = unsafe { &mut *(NLMSG_DATA!(nlh) as *mut ifaddrmsg) }; - - nlh.nlmsg_len = NLMSG_LENGTH!(mem::size_of::()); - nlh.nlmsg_type = RTM_GETADDR; - nlh.nlmsg_flags = NLM_F_DUMP | NLM_F_REQUEST; - self.assign_seqnum(nlh); - - ifa.ifa_family = libc::AF_UNSPEC as u8; - ifa.ifa_index = ifindex; - - self.send_message(v.as_mut_slice())?; - - self.recv_dump_message() - } - - fn dump_all_routes(&mut self) -> Result<(Vec>, Vec<*const nlmsghdr>)> { - let mut v: Vec = vec![0; 2048]; - // Safe because we have allocated enough buffer space. - let nlh = unsafe { &mut *(v.as_mut_ptr() as *mut nlmsghdr) }; - let rtm = unsafe { &mut *(NLMSG_DATA!(nlh) as *mut rtmsg) }; - - nlh.nlmsg_len = NLMSG_LENGTH!(mem::size_of::()) as u32; - nlh.nlmsg_type = RTM_GETROUTE; - nlh.nlmsg_flags = NLM_F_REQUEST | NLM_F_DUMP; - self.assign_seqnum(nlh); - - rtm.rtm_family = libc::AF_INET as u8; - rtm.rtm_table = RT_TABLE_MAIN as u8; - - // Safe because we have allocated enough buffer space. - unsafe { - nlh.addattr32(RTA_TABLE, RT_TABLE_MAIN); - } - - self.send_message(v.as_mut_slice())?; - - self.recv_dump_message() - } - - pub fn find_link_by_hwaddr(&mut self, hwaddr: &str) -> Result { - let hw = parser::parse_mac_addr(hwaddr)?; - let (_slv, lv) = self.dump_all_links()?; - - for link in &lv { - // Safe because dump_all_links() return valid pointers. - let nlh = unsafe { &**link }; - if nlh.nlmsg_type != RTM_NEWLINK && nlh.nlmsg_type != RTM_DELLINK { - continue; - } - - if nlh.nlmsg_len < NLMSG_SPACE!(mem::size_of::()) { - info!( - sl!(), - "invalid nlmsg! nlmsg_len: {}, nlmsg_space: {}", - nlh.nlmsg_len, - NLMSG_SPACE!(mem::size_of::()) - ); - break; - } - - let ifi: *const ifinfomsg = NLMSG_DATA!(nlh) as *const ifinfomsg; - let rta: *mut rtattr = IFLA_RTA!(ifi) as *mut rtattr; - let rtalen = IFLA_PAYLOAD!(nlh) as u32; - // Safe because IFLA_RTA and IFLA_PAYLOAD have validated the buffer. - unsafe { - let attrs = parse_attrs(rta, rtalen, (IFLA_MAX + 1) as usize)?; - - // find the target ifinfomsg - if !attrs[IFLA_ADDRESS as usize].is_null() { - let p = hw.as_ptr() as *const u8 as *const libc::c_void; - let a = RTA_DATA!(attrs[IFLA_ADDRESS as usize]) as *const libc::c_void; - let sz = RTA_PAYLOAD!(attrs[IFLA_ADDRESS as usize]) as libc::size_t; - if libc::memcmp(p, a, sz) == 0 { - return Ok(ifinfomsg { ..*ifi }); - } - } - } - } - - nix_errno(Errno::ENODEV) - } - - pub fn find_link_by_name(&mut self, name: &str) -> Result { - let mut v: Vec = vec![0; DEFAULT_NETLINK_BUF_SIZE]; - // Safe because we have allocated enough buffer size. - let nlh = unsafe { &mut *(v.as_mut_ptr() as *mut nlmsghdr) }; - let ifi = unsafe { &mut *(NLMSG_DATA!(nlh) as *mut ifinfomsg) }; - - nlh.nlmsg_len = NLMSG_LENGTH!(mem::size_of::()) as __u32; - nlh.nlmsg_type = RTM_GETLINK; - nlh.nlmsg_flags = NLM_F_REQUEST; - self.assign_seqnum(nlh); - - ifi.ifi_family = libc::AF_UNSPEC as u8; - - // Safe because the data buffer should be big enough. - unsafe { - if let Ok(cname) = CString::new(name.as_bytes()) { - nlh.addattr_var(IFLA_IFNAME, cname.as_bytes()); - } else { - return nix_errno(Errno::EINVAL); - } - nlh.addattr32(IFLA_EXT_MASK, RTEXT_FILTER_VF | RTEXT_FILTER_SKIP_STATS); - } - - let ret_val = self.rtnl_talk(v.as_mut_slice(), true)?; - if ret_val.len() < std::mem::size_of::() { - return nix_errno(Errno::EBADMSG); - } - // Safe because we have just validated the returned buffer size. - let nlh = unsafe { &*(ret_val.as_ptr() as *const nlmsghdr) }; - if nlh.nlmsg_len < NLMSG_LENGTH!(mem::size_of::()) { - return nix_errno(Errno::EBADMSG); - } - // Safe because we have just validated the returned buffer size. - let ifi = unsafe { &*(NLMSG_DATA!(nlh) as *const ifinfomsg) }; - - Ok(ifinfomsg { ..*ifi }) - } - - pub fn set_link_status(&mut self, ifinfo: &ifinfomsg, up: bool) -> Result<()> { - let mut v: Vec = vec![0; DEFAULT_NETLINK_BUF_SIZE]; - // Safe because we have allocated a big enough data buffer. - let nlh = unsafe { &mut *(v.as_mut_ptr() as *mut u8 as *mut nlmsghdr) }; - let ifi = unsafe { &mut *(NLMSG_DATA!(nlh) as *mut ifinfomsg) }; - - nlh.nlmsg_len = NLMSG_LENGTH!(mem::size_of::() as u32) as __u32; - nlh.nlmsg_type = RTM_NEWLINK; - nlh.nlmsg_flags = NLM_F_REQUEST; - self.assign_seqnum(nlh); - - ifi.ifi_family = ifinfo.ifi_family; - ifi.ifi_type = ifinfo.ifi_type; - ifi.ifi_index = ifinfo.ifi_index; - ifi.ifi_change |= libc::IFF_UP as u32; - if up { - ifi.ifi_flags |= libc::IFF_UP as u32; - } else { - ifi.ifi_flags &= !libc::IFF_UP as u32; - } - - self.rtnl_talk(v.as_mut_slice(), false).map(|_| ()) - } - - pub fn get_link_addresses(&mut self, ifinfo: &ifinfomsg) -> Result> { - let mut addrs: Vec = Vec::new(); - let (_sav, av) = self.dump_all_addresses(ifinfo.ifi_index as __u32)?; - - for a in &av { - // Safe because dump_all_addresses returns valid pointers. - let nlh = unsafe { &**a }; - if nlh.nlmsg_type != RTM_NEWADDR { - continue; - } - - let tlen = NLMSG_SPACE!(mem::size_of::()); - if nlh.nlmsg_len < tlen { - info!( - sl!(), - "invalid nlmsg! nlmsg_len: {}, nlmsg_space: {}", nlh.nlmsg_len, tlen - ); - break; - } - - // Safe because we have just validate the buffer size above. - let ifa = unsafe { &*(NLMSG_DATA!(nlh) as *const ifaddrmsg) }; - if ifa.ifa_flags as u32 & IFA_F_SECONDARY != 0 { - continue; - } - - let rta: *const rtattr = IFA_RTA!(ifa) as *mut rtattr; - let rtalen = IFA_PAYLOAD!(nlh) as u32; - - if ifinfo.ifi_index as u32 == ifa.ifa_index { - // Safe because dump_all_addresses returns valid pointers. - let addr = unsafe { - let attrs = parse_attrs(rta, rtalen, (IFA_MAX + 1) as usize)?; - let t = if !attrs[IFA_ADDRESS as usize].is_null() { - attrs[IFA_ADDRESS as usize] - } else { - attrs[IFA_LOCAL as usize] - }; - getattr_var(t as *const rtattr) - }; - - addrs.push(RtIPAddr { - ip_family: ifa.ifa_family, - ip_mask: ifa.ifa_prefixlen, - addr, - }); - } - } - - Ok(addrs) - } - - pub fn delete_one_addr(&mut self, ifinfo: &ifinfomsg, addr: &RtIPAddr) -> Result<()> { - let mut v: Vec = vec![0; DEFAULT_NETLINK_BUF_SIZE]; - // Safe because we have allocated a big enough data buffer. - let nlh = unsafe { &mut *(v.as_mut_ptr() as *mut nlmsghdr) }; - let ifa = unsafe { &mut *(NLMSG_DATA!(nlh) as *mut ifaddrmsg) }; - - nlh.nlmsg_len = NLMSG_LENGTH!(mem::size_of::() as u32) as __u32; - nlh.nlmsg_type = RTM_DELADDR; - nlh.nlmsg_flags = NLM_F_REQUEST; - self.assign_seqnum(nlh); - - ifa.ifa_family = addr.ip_family; - ifa.ifa_prefixlen = addr.ip_mask; - ifa.ifa_index = ifinfo.ifi_index as u32; - - // Safe because we have allocated a big enough data buffer. - unsafe { - nlh.addattr_var(IFA_ADDRESS, addr.addr.as_ref()); - } - - // ignore EADDRNOTAVAIL here.. - self.rtnl_talk(v.as_mut_slice(), false)?; - - Ok(()) - } - - pub fn delete_all_addrs(&mut self, ifinfo: &ifinfomsg, addrs: &[RtIPAddr]) -> Result<()> { - for a in addrs { - self.delete_one_addr(ifinfo, a)?; - } - - Ok(()) - } - - pub fn add_one_address(&mut self, ifinfo: &ifinfomsg, ip: &RtIPAddr) -> Result<()> { - let mut v: Vec = vec![0; DEFAULT_NETLINK_BUF_SIZE]; - // Safe because we have allocated a big enough data buffer. - let nlh = unsafe { &mut *(v.as_mut_ptr() as *mut nlmsghdr) }; - let ifa = unsafe { &mut *(NLMSG_DATA!(nlh) as *mut ifaddrmsg) }; - - nlh.nlmsg_len = NLMSG_LENGTH!(mem::size_of::() as u32) as __u32; - nlh.nlmsg_type = RTM_NEWADDR; - nlh.nlmsg_flags = NLM_F_REQUEST | NLM_F_CREATE | NLM_F_EXCL; - self.assign_seqnum(nlh); - - ifa.ifa_family = ip.ip_family; - ifa.ifa_prefixlen = ip.ip_mask; - ifa.ifa_index = ifinfo.ifi_index as __u32; - - // Safe because we have allocated a big enough data buffer. - unsafe { - nlh.addattr_var(IFA_ADDRESS, ip.addr.as_ref()); - // don't know why need IFA_LOCAL, without it kernel returns -EINVAL... - nlh.addattr_var(IFA_LOCAL, ip.addr.as_ref()); - } - - self.rtnl_talk(v.as_mut_slice(), false)?; - - Ok(()) - } - - pub fn get_name_by_index(&mut self, index: i32) -> Result { - let mut v: Vec = vec![0; DEFAULT_NETLINK_BUF_SIZE]; - let mut i = 0; - - while i < 5 { - i += 1; - // Safe because we have allocated enough buffer space. - let nlh = unsafe { &mut *(v.as_mut_ptr() as *mut nlmsghdr) }; - let ifi = unsafe { &mut *(NLMSG_DATA!(nlh) as *mut ifinfomsg) }; - - nlh.nlmsg_len = NLMSG_LENGTH!(mem::size_of::()) as __u32; - nlh.nlmsg_type = RTM_GETLINK; - nlh.nlmsg_flags = NLM_F_REQUEST; - self.assign_seqnum(nlh); - - ifi.ifi_index = index; - - unsafe { - nlh.addattr32(IFLA_EXT_MASK, RTEXT_FILTER_VF | RTEXT_FILTER_SKIP_STATS); - } - - let mut retv = self.rtnl_talk(v.as_mut_slice(), true)?; - if retv.len() < std::mem::size_of::() { - return nix_errno(Errno::EBADMSG); - } - - let nlh = unsafe { &mut *(retv.as_mut_ptr() as *mut nlmsghdr) }; - if nlh.nlmsg_type != RTM_NEWLINK && nlh.nlmsg_type != RTM_DELLINK { - info!(sl!(), "wrong message!"); - continue; - } - - let tlen = NLMSG_SPACE!(mem::size_of::()); - if nlh.nlmsg_len < tlen { - info!(sl!(), "corrupt message?"); - continue; - } - - let ifi = unsafe { &mut *(NLMSG_DATA!(nlh) as *mut ifinfomsg) }; - let rta: *mut rtattr = IFLA_RTA!(ifi) as *mut rtattr; - let rtalen = IFLA_PAYLOAD!(nlh) as u32; - - let attrs = unsafe { parse_attrs(rta, rtalen, (IFLA_MAX + 1) as usize)? }; - - let t = attrs[IFLA_IFNAME as usize]; - if !t.is_null() { - // we have a name - let tdata = unsafe { getattr_var(t) }; - return Ok(String::from_utf8(tdata)?); - } - } - - nix_errno(Errno::ENOENT) - } - - pub fn get_all_routes(&mut self) -> Result> { - let mut rs: Vec = Vec::new(); - let (_srv, rv) = self.dump_all_routes()?; - - for r in &rv { - // Safe because dump_all_routes() return valid pointers. - let nlh = unsafe { &**r }; - let rtm = unsafe { &mut *(NLMSG_DATA!(nlh) as *mut rtmsg) }; - - if nlh.nlmsg_type != RTM_NEWROUTE && nlh.nlmsg_type != RTM_DELROUTE { - info!(sl!(), "not route message!"); - continue; - } - - let tlen = NLMSG_SPACE!(mem::size_of::()); - if nlh.nlmsg_len < tlen { - info!( - sl!(), - "invalid nlmsg! nlmsg_len: {}, nlmsg_spae: {}", nlh.nlmsg_len, tlen - ); - break; - } - - if rtm.rtm_table != RT_TABLE_MAIN as u8 { - continue; - } - - let rta: *mut rtattr = RTM_RTA!(rtm) as *mut rtattr; - let rtalen = RTM_PAYLOAD!(nlh) as u32; - - let attrs = unsafe { parse_attrs(rta, rtalen, (RTA_MAX + 1) as usize)? }; - - let t = attrs[RTA_TABLE as usize]; - if !t.is_null() { - let table = unsafe { getattr32(t) }; - if table != RT_TABLE_MAIN { - continue; - } - } - - // find source, destination, gateway, scope, and device name - let mut t = attrs[RTA_DST as usize]; - let mut rte: RtRoute = RtRoute::default(); - - rte.dst_len = (*rtm).rtm_dst_len; - rte.src_len = (*rtm).rtm_src_len; - rte.dest = None; - rte.protocol = (*rtm).rtm_protocol; - // destination - if !t.is_null() { - rte.dest = Some(unsafe { getattr_var(t as *const rtattr) }); - } - - // gateway - t = attrs[RTA_GATEWAY as usize]; - if !t.is_null() { - rte.gateway = Some(unsafe { getattr_var(t as *const rtattr) }); - if rte.dest.is_none() { - rte.dest = Some(vec![0 as u8; 4]); - } - } - - // source - t = attrs[RTA_SRC as usize]; - if t.is_null() { - t = attrs[RTA_PREFSRC as usize]; - } - if !t.is_null() { - rte.source = Some(unsafe { getattr_var(t as *const rtattr) }); - } - - // scope - rte.scope = rtm.rtm_scope; - - // oif - t = attrs[RTA_OIF as usize]; - if !t.is_null() { - rte.index = unsafe { getattr32(t as *const rtattr) as i32 }; - } - - rs.push(rte); - } - - Ok(rs) - } - - pub fn delete_all_routes(&mut self, rs: &[RtRoute]) -> Result<()> { - for r in rs { - let name = self.get_name_by_index(r.index)?; - if name.as_str().contains("lo") || name.as_str().contains("::1") { - continue; - } - - if r.protocol == RTPROTO_KERNEL { - continue; - } - - self.delete_one_route(r)?; - } - - Ok(()) - } - - pub fn add_one_route(&mut self, r: &RtRoute) -> Result<()> { - let mut v: Vec = vec![0; DEFAULT_NETLINK_BUF_SIZE]; - // Safe because we have allocated a big enough data buffer. - let nlh = unsafe { &mut *(v.as_mut_ptr() as *mut nlmsghdr) }; - let rtm = unsafe { &mut *(NLMSG_DATA!(nlh) as *mut rtmsg) }; - - nlh.nlmsg_len = NLMSG_LENGTH!(mem::size_of::()) as u32; - nlh.nlmsg_type = RTM_NEWROUTE; - nlh.nlmsg_flags = NLM_F_REQUEST | NLM_F_CREATE | NLM_F_EXCL; - self.assign_seqnum(nlh); - - rtm.rtm_family = libc::AF_INET as u8; - rtm.rtm_table = RT_TABLE_MAIN as u8; - rtm.rtm_scope = RT_SCOPE_NOWHERE; - rtm.rtm_protocol = RTPROTO_BOOT; - rtm.rtm_scope = RT_SCOPE_UNIVERSE; - rtm.rtm_type = RTN_UNICAST; - - rtm.rtm_dst_len = r.dst_len; - rtm.rtm_src_len = r.src_len; - rtm.rtm_scope = r.scope; - - unsafe { - if let Some(source) = r.source.as_ref() { - if r.src_len > 0 { - nlh.addattr_var(RTA_SRC, source.as_ref()); - } else { - nlh.addattr_var(RTA_PREFSRC, source.as_ref()); - } - } - - if let Some(dest) = r.dest.as_ref() { - nlh.addattr_var(RTA_DST, dest.as_ref()); - } - - if let Some(gateway) = r.gateway.as_ref() { - nlh.addattr_var(RTA_GATEWAY, gateway.as_ref()); - } - - nlh.addattr32(RTA_OIF, r.index as u32); - } - - self.rtnl_talk(v.as_mut_slice(), false)?; - - Ok(()) - } - - pub fn delete_one_route(&mut self, r: &RtRoute) -> Result<()> { - info!(sl!(), "delete route"); - let mut v: Vec = vec![0; DEFAULT_NETLINK_BUF_SIZE]; - // Safe because we have allocated a big enough data buffer. - let nlh = unsafe { &mut *(v.as_mut_ptr() as *mut nlmsghdr) }; - let rtm = unsafe { &mut *(NLMSG_DATA!(nlh) as *mut rtmsg) }; - - nlh.nlmsg_len = NLMSG_LENGTH!(mem::size_of::()) as u32; - nlh.nlmsg_type = RTM_DELROUTE; - nlh.nlmsg_flags = NLM_F_REQUEST; - self.assign_seqnum(nlh); - - rtm.rtm_family = libc::AF_INET as u8; - rtm.rtm_table = RT_TABLE_MAIN as u8; - rtm.rtm_scope = RT_SCOPE_NOWHERE; - - rtm.rtm_dst_len = r.dst_len; - rtm.rtm_src_len = r.src_len; - rtm.rtm_scope = r.scope; - - // Safe because we have allocated a big enough data buffer. - unsafe { - if let Some(source) = r.source.as_ref() { - if r.src_len > 0 { - nlh.addattr_var(RTA_SRC, source.as_ref()); - } else { - nlh.addattr_var(RTA_PREFSRC, source.as_ref()); - } - } - - if let Some(dest) = r.dest.as_ref() { - nlh.addattr_var(RTA_DST, dest.as_ref()); - } - - if let Some(gateway) = r.gateway.as_ref() { - nlh.addattr_var(RTA_GATEWAY, gateway.as_ref()); - } - - nlh.addattr32(RTA_OIF, r.index as u32); - } - - self.rtnl_talk(v.as_mut_slice(), false)?; - - Ok(()) - } - - pub fn handle_localhost(&mut self) -> Result<()> { - let ifi = self.find_link_by_name("lo")?; - - self.set_link_status(&ifi, true) - } - - fn assign_seqnum(&mut self, nlh: &mut nlmsghdr) { - self.seq += 1; - self.dump = self.seq; - nlh.nlmsg_seq = self.seq; - } -} - -impl Drop for RtnlHandle { - fn drop(&mut self) { - if self.fd >= 0 { - unsafe { - libc::close(self.fd); - } - self.fd = -1; - } - } -} - -/// Parse netlink attributes from raw buffer. -/// -/// # Safety -/// Caller needs to ensure that rta and rtalen are valid. -pub unsafe fn parse_attrs( - mut rta: *const rtattr, - mut rtalen: u32, - max: usize, -) -> Result> { - let mut attrs: Vec<*const rtattr> = vec![std::ptr::null(); max as usize]; - - while RTA_OK!(rta, rtalen) { - let rtype = (*rta).rta_type as usize; - - if rtype < max && attrs[rtype].is_null() { - attrs[rtype] = rta as *const rtattr; - } - - rta = RTA_NEXT!(rta, rtalen) - } - - Ok(attrs) -} - -impl nlmsghdr { - /// Add an variable attribute to the netlink message. - /// - /// # Safety - /// Caller needs to ensure that there are enough space after `self` to store the attribute. - pub unsafe fn addattr_var(&mut self, cat: u16, data: &[u8]) { - let mut rta: *mut rtattr = NLMSG_TAIL!(self) as *mut rtattr; - let alen = RTA_LENGTH!(data.len()) as u16; - - (*rta).rta_type = cat; - (*rta).rta_len = alen; - - if !data.is_empty() { - libc::memcpy( - RTA_DATA!(rta) as *mut libc::c_void, - data.as_ptr() as *const libc::c_void, - data.len(), - ); - } - - self.nlmsg_len = NLMSG_ALIGN!(self.nlmsg_len) + RTA_ALIGN!(alen); - } - - /// Add a string attribute to the netlink message. - /// - /// # Safety - /// Caller needs to ensure that there are enough space after `self` to store the attribute. - pub unsafe fn addattr_str(&mut self, cat: u16, data: &str) { - let mut rta: *mut rtattr = NLMSG_TAIL!(self) as *mut rtattr; - let len = data.len(); - let alen = RTA_LENGTH!(len + 1) as u16; - let tp: *mut libc::c_void = RTA_DATA!(rta) as *mut libc::c_void; - - (*rta).rta_type = cat; - (*rta).rta_len = alen; - - libc::memcpy( - tp, - data.as_ptr() as *const libc::c_void, - len as libc::size_t, - ); - - self.nlmsg_len = NLMSG_ALIGN!(self.nlmsg_len) + RTA_ALIGN!(alen); - } - - /// Add an 1/2/4/8 bytes attribute to the netlink message. - /// - /// # Safety - /// Caller needs to ensure that there are enough space after `self` to store the attribute. - pub unsafe fn addattr_size(&mut self, cat: u16, val: u64, size: u8) { - assert_eq!(size == 1 || size == 2 || size == 4 || size == 8, true); - - let mut rta: *mut rtattr = NLMSG_TAIL!(self) as *mut rtattr; - (*rta).rta_type = cat; - - if size == 1 { - let data: *mut u8 = RTA_DATA!(rta) as *mut u8; - *data = val as u8; - let len = RTA_LENGTH!(1) as u16; - (*rta).rta_len = len; - } - - if size == 2 { - let data: *mut u16 = RTA_DATA!(rta) as *mut u16; - *data = val as u16; - let len = RTA_LENGTH!(2) as u16; - (*rta).rta_len = len; - } - - if size == 4 { - let data: *mut u32 = RTA_DATA!(rta) as *mut u32; - *data = val as u32; - let len = RTA_LENGTH!(4) as u16; - (*rta).rta_len = len; - } - - if size == 8 { - let data: *mut u64 = RTA_DATA!(rta) as *mut u64; - *data = val as u64; - let len = RTA_LENGTH!(8) as u16; - (*rta).rta_len = len; - } - - self.nlmsg_len = NLMSG_ALIGN!(self.nlmsg_len) + RTA_ALIGN!((*rta).rta_len); - } - - /// Add a 8-bit attribute. - /// - /// # Safety - /// Caller needs to ensure that there are enough space after `self` to store the attribute. - pub unsafe fn addattr8(&mut self, cat: u16, val: u8) { - self.addattr_size(cat, val as u64, 1); - } - - /// Add a 16-bit attribute. - /// - /// # Safety - /// Caller needs to ensure that there are enough space after `self` to store the attribute. - pub unsafe fn addattr16(&mut self, cat: u16, val: u16) { - self.addattr_size(cat, val as u64, 2); - } - - /// Add a 32-bit attribute. - /// - /// # Safety - /// Caller needs to ensure that there are enough space after `self` to store the attribute. - pub unsafe fn addattr32(&mut self, cat: u16, val: u32) { - self.addattr_size(cat, val as u64, 4); - } - - /// Add a 64-bit attribute. - /// - /// # Safety - /// Caller needs to ensure that there are enough space after `self` to store the attribute. - pub unsafe fn addattr64(&mut self, cat: u16, val: u64) { - self.addattr_size(cat, val, 8); - } -} - -unsafe fn getattr_size(rta: *const rtattr) -> u64 { - let alen: usize = RTA_PAYLOAD!(rta) as usize; - assert!(alen == 1 || alen == 2 || alen == 4 || alen == 8); - let tp: *const u8 = RTA_DATA!(rta) as *const u8; - - if alen == 1 { - let data: *const u8 = tp as *const u8; - return *data as u64; - } - - if alen == 2 { - let data: *const u16 = tp as *const u16; - return *data as u64; - } - - if alen == 4 { - let data: *const u32 = tp as *const u32; - return *data as u64; - } - - if alen == 8 { - let data: *const u64 = tp as *const u64; - return *data; - } - - panic!("impossible!"); -} - -/// Get a 8-bit attribute. -/// -/// # Safety -/// Caller needs to ensure that there are enough space after `rta` to read the attribute. -pub unsafe fn getattr8(rta: *const rtattr) -> u8 { - let alen = RTA_PAYLOAD!(rta); - assert_eq!(alen, 1); - getattr_size(rta) as u8 -} - -/// Get a 16-bit attribute. -/// -/// # Safety -/// Caller needs to ensure that there are enough space after `rta` to read the attribute. -pub unsafe fn getattr16(rta: *const rtattr) -> u16 { - let alen = RTA_PAYLOAD!(rta); - assert_eq!(alen, 2); - getattr_size(rta) as u16 -} - -/// Get a 32-bit attribute. -/// -/// # Safety -/// Caller needs to ensure that there are enough space after `rta` to read the attribute. -pub unsafe fn getattr32(rta: *const rtattr) -> u32 { - let alen = RTA_PAYLOAD!(rta); - assert_eq!(alen, 4); - getattr_size(rta) as u32 -} - -/// Get a 64-bit attribute. -/// -/// # Safety -/// Caller needs to ensure that there are enough space after `rta` to read the attribute. -pub unsafe fn getattr64(rta: *const rtattr) -> u64 { - let alen = RTA_PAYLOAD!(rta); - assert_eq!(alen, 8); - getattr_size(rta) -} - -/// Get a variable length attribute. -/// -/// # Safety -/// Caller needs to ensure that there are enough space after `rta` to read the attribute. -pub unsafe fn getattr_var(rta: *const rtattr) -> Vec { - assert_ne!(rta as i64, 0); - let data: *const libc::c_void = RTA_DATA!(rta) as *const libc::c_void; - let alen: usize = RTA_PAYLOAD!(rta) as usize; - - let mut v: Vec = vec![0; alen]; - let tp: *mut libc::c_void = v.as_mut_ptr() as *mut libc::c_void; - - libc::memcpy(tp, data, alen as libc::size_t); - - v -} - -#[inline] -fn nix_last_os_err() -> Result { - Err(nix::Error::Sys(Errno::last())) -} - -#[inline] -fn nix_errno(err: Errno) -> Result { - Err(nix::Error::Sys(err)) -} - -#[cfg(test)] -mod tests { - use super::*; - use libc; - use std::mem; - - #[test] - fn test_macro() { - println!("{}", RTA_ALIGN!(10)); - assert_eq!(RTA_ALIGN!(6), 8); - assert_eq!(RTM_FAM!(36), 5); - assert_eq!( - NLMSG_HDRLEN!(), - NLMSG_ALIGN!(mem::size_of::() as libc::c_uint) - ); - } -} diff --git a/src/agent/netlink/src/parser.rs b/src/agent/netlink/src/parser.rs deleted file mode 100644 index 0786c0396b..0000000000 --- a/src/agent/netlink/src/parser.rs +++ /dev/null @@ -1,201 +0,0 @@ -// Copyright (c) 2019 Ant Financial -// -// SPDX-License-Identifier: Apache-2.0 - -//! Parser for IPv4/IPv6/MAC addresses. - -use std::net::{Ipv4Addr, Ipv6Addr}; -use std::str::FromStr; - -use super::{Errno, Result, __u8, nix_errno}; - -#[inline] -pub(crate) fn parse_u8(s: &str, radix: u32) -> Result { - if radix >= 2 && radix <= 36 { - u8::from_str_radix(s, radix).map_err(|_| nix::Error::Sys(Errno::EINVAL)) - } else { - u8::from_str(s).map_err(|_| nix::Error::Sys(Errno::EINVAL)) - } -} - -pub fn parse_ipv4_addr(s: &str) -> Result> { - match Ipv4Addr::from_str(s) { - Ok(v) => Ok(Vec::from(v.octets().as_ref())), - Err(_e) => nix_errno(Errno::EINVAL), - } -} - -pub fn parse_ip_addr(s: &str) -> Result> { - if let Ok(v6) = Ipv6Addr::from_str(s) { - Ok(Vec::from(v6.octets().as_ref())) - } else { - parse_ipv4_addr(s) - } -} - -pub fn parse_ip_addr_with_family(ip_address: &str) -> Result<(__u8, Vec)> { - if let Ok(v6) = Ipv6Addr::from_str(ip_address) { - Ok((libc::AF_INET6 as __u8, Vec::from(v6.octets().as_ref()))) - } else { - parse_ipv4_addr(ip_address).map(|v| (libc::AF_INET as __u8, v)) - } -} - -pub fn parse_ipv4_cidr(s: &str) -> Result<(Vec, u8)> { - let fields: Vec<&str> = s.split('/').collect(); - - if fields.len() != 2 { - nix_errno(Errno::EINVAL) - } else { - Ok((parse_ipv4_addr(fields[0])?, parse_u8(fields[1], 10)?)) - } -} - -pub fn parse_cidr(s: &str) -> Result<(Vec, u8)> { - let fields: Vec<&str> = s.split('/').collect(); - - if fields.len() != 2 { - nix_errno(Errno::EINVAL) - } else { - Ok((parse_ip_addr(fields[0])?, parse_u8(fields[1], 10)?)) - } -} - -pub fn parse_mac_addr(hwaddr: &str) -> Result> { - let fields: Vec<&str> = hwaddr.split(':').collect(); - - if fields.len() != 6 { - nix_errno(Errno::EINVAL) - } else { - Ok(vec![ - parse_u8(fields[0], 16)?, - parse_u8(fields[1], 16)?, - parse_u8(fields[2], 16)?, - parse_u8(fields[3], 16)?, - parse_u8(fields[4], 16)?, - parse_u8(fields[5], 16)?, - ]) - } -} - -/// Format an IPv4/IPv6/MAC address. -/// -/// # Safety -/// Caller needs to ensure that addr and len are valid. -pub unsafe fn format_address(addr: *const u8, len: u32) -> Result { - let mut a: String; - if len == 4 { - // ipv4 - let mut i = 1; - let mut p = addr as i64; - - a = format!("{}", *(p as *const u8)); - while i < len { - p += 1; - i += 1; - a.push_str(format!(".{}", *(p as *const u8)).as_str()); - } - - return Ok(a); - } - - if len == 6 { - // hwaddr - let mut i = 1; - let mut p = addr as i64; - - a = format!("{:0>2X}", *(p as *const u8)); - while i < len { - p += 1; - i += 1; - a.push_str(format!(":{:0>2X}", *(p as *const u8)).as_str()); - } - - return Ok(a); - } - - if len == 16 { - // ipv6 - let p = addr as *const u8 as *const libc::c_void; - let mut ar: [u8; 16] = [0; 16]; - let mut v: Vec = vec![0; 16]; - let dp: *mut libc::c_void = v.as_mut_ptr() as *mut libc::c_void; - libc::memcpy(dp, p, 16); - - ar.copy_from_slice(v.as_slice()); - - return Ok(Ipv6Addr::from(ar).to_string()); - } - - nix_errno(Errno::EINVAL) -} - -#[cfg(test)] -mod tests { - use super::*; - use libc; - - #[test] - fn test_ip_addr() { - let ip = parse_ipv4_addr("1.2.3.4").unwrap(); - assert_eq!(ip, vec![0x1u8, 0x2u8, 0x3u8, 0x4u8]); - parse_ipv4_addr("1.2.3.4.5").unwrap_err(); - parse_ipv4_addr("1.2.3-4").unwrap_err(); - parse_ipv4_addr("1.2.3.a").unwrap_err(); - parse_ipv4_addr("1.2.3.x").unwrap_err(); - parse_ipv4_addr("-1.2.3.4").unwrap_err(); - parse_ipv4_addr("+1.2.3.4").unwrap_err(); - - let (family, _) = parse_ip_addr_with_family("192.168.1.1").unwrap(); - assert_eq!(family, libc::AF_INET as __u8); - - let (family, ip) = - parse_ip_addr_with_family("2001:0db8:85a3:0000:0000:8a2e:0370:7334").unwrap(); - assert_eq!(family, libc::AF_INET6 as __u8); - assert_eq!(ip.len(), 16); - parse_ip_addr_with_family("2001:0db8:85a3:0000:0000:8a2e:0370:73345").unwrap_err(); - - let ip = parse_ip_addr("::1").unwrap(); - assert_eq!(ip[0], 0x0); - assert_eq!(ip[15], 0x1); - } - - #[test] - fn test_parse_cidr() { - let (_, mask) = parse_ipv4_cidr("1.2.3.4/31").unwrap(); - assert_eq!(mask, 31); - - parse_ipv4_cidr("1.2.3/4/31").unwrap_err(); - parse_ipv4_cidr("1.2.3.4/f").unwrap_err(); - parse_ipv4_cidr("1.2.3/8").unwrap_err(); - parse_ipv4_cidr("1.2.3.4.8").unwrap_err(); - - let (ip, mask) = parse_cidr("2001:db8:a::123/64").unwrap(); - assert_eq!(mask, 64); - assert_eq!(ip[0], 0x20); - assert_eq!(ip[15], 0x23); - } - - #[test] - fn test_parse_mac_addr() { - let mac = parse_mac_addr("FF:FF:FF:FF:FF:FE").unwrap(); - assert_eq!(mac.len(), 6); - assert_eq!(mac[0], 0xff); - assert_eq!(mac[5], 0xfe); - - parse_mac_addr("FF:FF:FF:FF:FF:FE:A0").unwrap_err(); - parse_mac_addr("FF:FF:FF:FF:FF:FX").unwrap_err(); - parse_mac_addr("FF:FF:FF:FF:FF").unwrap_err(); - } - - #[test] - fn test_format_address() { - let buf = [1u8, 2u8, 3u8, 4u8]; - let addr = unsafe { format_address(&buf as *const u8, 4).unwrap() }; - assert_eq!(addr, "1.2.3.4"); - - let buf = [1u8, 2u8, 3u8, 4u8, 5u8, 6u8]; - let addr = unsafe { format_address(&buf as *const u8, 6).unwrap() }; - assert_eq!(addr, "01:02:03:04:05:06"); - } -} diff --git a/src/agent/src/main.rs b/src/agent/src/main.rs index 2073d1b2b3..cf7248f349 100644 --- a/src/agent/src/main.rs +++ b/src/agent/src/main.rs @@ -24,9 +24,7 @@ extern crate scopeguard; #[macro_use] extern crate slog; -extern crate netlink; -use crate::netlink::{RtnlHandle, NETLINK_ROUTE}; use anyhow::{anyhow, Context, Result}; use nix::fcntl::{self, OFlag}; use nix::fcntl::{FcntlArg, FdFlag}; @@ -55,6 +53,7 @@ mod linux_abi; mod metrics; mod mount; mod namespace; +mod netlink; mod network; pub mod random; mod sandbox; @@ -276,13 +275,9 @@ async fn start_sandbox(logger: &Logger, config: &agentConfig, init_mode: bool) - }; // Initialize unique sandbox structure. - let mut s = Sandbox::new(&logger).context("Failed to create sandbox")?; - + let s = Sandbox::new(&logger).context("Failed to create sandbox")?; if init_mode { - let mut rtnl = RtnlHandle::new(NETLINK_ROUTE, 0).unwrap(); - rtnl.handle_localhost()?; - - s.rtnl = Some(rtnl); + s.rtnl.handle_localhost().await?; } let sandbox = Arc::new(Mutex::new(s)); diff --git a/src/agent/src/netlink.rs b/src/agent/src/netlink.rs new file mode 100644 index 0000000000..274487a6c6 --- /dev/null +++ b/src/agent/src/netlink.rs @@ -0,0 +1,1038 @@ +// Copyright (c) 2021 Kata Maintainers +// +// SPDX-License-Identifier: Apache-2.0 +// + +use anyhow::{anyhow, Context, Result}; +use futures::{future, StreamExt, TryStreamExt}; +use ipnetwork::{IpNetwork, Ipv4Network, Ipv6Network}; +use protobuf::RepeatedField; +use protocols::types::{ARPNeighbor, IPAddress, IPFamily, Interface, Route}; +use rtnetlink::{new_connection, packet, IpVersion}; +use std::convert::{TryFrom, TryInto}; +use std::fmt; +use std::net::{IpAddr, Ipv4Addr, Ipv6Addr}; +use std::ops::Deref; +use std::str::{self, FromStr}; + +/// Search criteria to use when looking for a link in `find_link`. +pub enum LinkFilter<'a> { + /// Find by link name. + Name(&'a str), + /// Find by link index. + Index(u32), + /// Find by MAC address. + Address(&'a str), +} + +impl fmt::Display for LinkFilter<'_> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + LinkFilter::Name(name) => write!(f, "Name: {}", name), + LinkFilter::Index(idx) => write!(f, "Index: {}", idx), + LinkFilter::Address(addr) => write!(f, "Address: {}", addr), + } + } +} + +/// A filter to query addresses. +pub enum AddressFilter { + /// Return addresses that belong to the given interface. + LinkIndex(u32), + /// Get addresses with the given prefix. + IpAddress(IpAddr), +} + +/// A high level wrapper for netlink (and `rtnetlink` crate) for use by the Agent's RPC. +/// It is expected to be consumed by the `agentService`, so it operates with protobuf +/// structures directly for convenience. +#[derive(Debug)] +pub struct Handle { + handle: rtnetlink::Handle, +} + +impl Handle { + pub(crate) fn new() -> Result { + let (conn, handle, _) = new_connection()?; + tokio::spawn(conn); + + Ok(Handle { handle }) + } + + pub async fn update_interface(&mut self, iface: &Interface) -> Result<()> { + // The reliable way to find link is using hardware address + // as filter. However, hardware filter might not be supported + // by netlink, we may have to dump link list and the find the + // target link. filter using name or family is supported, but + // we cannot use that to find target link. + // let's try if hardware address filter works. -_- + let link = self.find_link(LinkFilter::Address(&iface.hwAddr)).await?; + + // Bring down interface if it is UP + if link.is_up() { + self.enable_link(link.index(), false).await?; + } + + // Delete all addresses associated with the link + let addresses = self + .list_addresses(AddressFilter::LinkIndex(link.index())) + .await?; + self.delete_addresses(addresses).await?; + + // Add new ip addresses from request + for ip_address in &iface.IPAddresses { + let ip = IpAddr::from_str(&ip_address.get_address())?; + let mask = u8::from_str_radix(ip_address.get_mask(), 10)?; + + self.add_addresses(link.index(), std::iter::once(IpNetwork::new(ip, mask)?)) + .await?; + } + + // Update link + let mut request = self.handle.link().set(link.index()); + request.message_mut().header = link.header.clone(); + + request + .mtu(iface.mtu as _) + .name(iface.name.clone()) + .arp(iface.raw_flags & libc::IFF_NOARP as u32 == 0) + .up() + .execute() + .await?; + + Ok(()) + } + + pub async fn handle_localhost(&self) -> Result<()> { + let link = self.find_link(LinkFilter::Name("lo")).await?; + self.enable_link(link.index(), true).await?; + Ok(()) + } + + pub async fn update_routes(&mut self, list: I) -> Result<()> + where + I: IntoIterator, + { + let old_routes = self + .query_routes(None) + .await + .with_context(|| "Failed to query old routes")?; + + self.delete_routes(old_routes) + .await + .with_context(|| "Failed to delete old routes")?; + + self.add_routes(list) + .await + .with_context(|| "Failed to add new routes")?; + + Ok(()) + } + + /// Retireve available network interfaces. + pub async fn list_interfaces(&self) -> Result> { + let mut list = Vec::new(); + + let links = self.list_links().await?; + + for link in &links { + let mut iface = Interface { + name: link.name(), + hwAddr: link.address(), + mtu: link.mtu().unwrap_or(0), + ..Default::default() + }; + + let ips = self + .list_addresses(AddressFilter::LinkIndex(link.index())) + .await? + .into_iter() + .map(|p| p.try_into()) + .collect::>>()?; + + iface.IPAddresses = RepeatedField::from_vec(ips); + + list.push(iface); + } + + Ok(list) + } + + async fn find_link(&self, filter: LinkFilter<'_>) -> Result { + let request = self.handle.link().get(); + + let filtered = match filter { + LinkFilter::Name(name) => request.set_name_filter(name.to_owned()), + LinkFilter::Index(index) => request.match_index(index), + _ => request, // Post filters + }; + + let mut stream = filtered.execute(); + + let next = if let LinkFilter::Address(addr) = filter { + use packet::link::nlas::Nla; + + let mac_addr = parse_mac_address(addr) + .with_context(|| format!("Failed to parse MAC address: {}", addr))?; + + // Hardware filter might not be supported by netlink, + // we may have to dump link list and the find the target link. + stream + .try_filter(|f| { + let result = f.nlas.iter().any(|n| match n { + Nla::Address(data) => data.eq(&mac_addr), + _ => false, + }); + + future::ready(result) + }) + .try_next() + .await? + } else { + stream.try_next().await? + }; + + next.map(|msg| msg.into()) + .ok_or_else(|| anyhow!("Link not found ({})", filter)) + } + + async fn list_links(&self) -> Result> { + let result = self + .handle + .link() + .get() + .execute() + .try_filter_map(|msg| future::ready(Ok(Some(msg.into())))) // Don't filter, just map + .try_collect::>() + .await?; + Ok(result) + } + + pub async fn enable_link(&self, link_index: u32, up: bool) -> Result<()> { + let link_req = self.handle.link().set(link_index); + let set_req = if up { link_req.up() } else { link_req.down() }; + set_req.execute().await?; + Ok(()) + } + + pub async fn delete_links(&mut self, list: I) -> Result<()> + where + I: IntoIterator, + { + for index in list.into_iter() { + self.handle.link().del(index).execute().await?; + } + + Ok(()) + } + + async fn query_routes( + &self, + ip_version: Option, + ) -> Result> { + let list = if let Some(ip_version) = ip_version { + self.handle + .route() + .get(ip_version) + .execute() + .try_collect() + .await? + } else { + // These queries must be executed sequentially, otherwise + // it'll throw "Device or resource busy (os error 16)" + let routes4 = self + .handle + .route() + .get(IpVersion::V4) + .execute() + .try_collect::>() + .await + .with_context(|| "Failed to query IP v4 routes")?; + + let routes6 = self + .handle + .route() + .get(IpVersion::V6) + .execute() + .try_collect::>() + .await + .with_context(|| "Failed to query IP v6 routes")?; + + [routes4, routes6].concat() + }; + + Ok(list) + } + + pub async fn list_routes(&self) -> Result> { + let mut result = Vec::new(); + + for msg in self.query_routes(None).await? { + // Ignore non-main tables + if msg.header.table != packet::constants::RT_TABLE_MAIN { + continue; + } + + let mut route = Route { + scope: msg.header.scope as _, + ..Default::default() + }; + + if let Some((ip, mask)) = msg.destination_prefix() { + route.dest = format!("{}/{}", ip, mask); + } + + if let Some((ip, mask)) = msg.source_prefix() { + route.source = format!("{}/{}", ip, mask); + } + + if let Some(addr) = msg.gateway() { + route.gateway = addr.to_string(); + + // For gateway, destination is 0.0.0.0 + route.dest = if addr.is_ipv4() { + String::from("0.0.0.0") + } else { + String::from("::1") + } + } + + if let Some(index) = msg.output_interface() { + route.device = self.find_link(LinkFilter::Index(index)).await?.name(); + } + + result.push(route); + } + + Ok(result) + } + + /// Adds a list of routes from iterable object `I`. + /// It can accept both a collection of routes or a single item (via `iter::once()`). + /// It'll also take care of proper order when adding routes (gateways first, everything else after). + async fn add_routes(&mut self, list: I) -> Result<()> + where + I: IntoIterator, + { + // Split the list so we add routes with no gateway first. + // Note: `partition_in_place` is a better fit here, since it reorders things inplace (instead of + // allocating two separate collections), however it's not yet in stable Rust. + let (a, b): (Vec, Vec) = list.into_iter().partition(|p| p.gateway.is_empty()); + let list = a.iter().chain(&b); + + for route in list { + let link = self.find_link(LinkFilter::Name(&route.device)).await?; + let is_v6 = is_ipv6(route.get_gateway()) || is_ipv6(route.get_dest()); + + const MAIN_TABLE: u8 = packet::constants::RT_TABLE_MAIN; + const UNICAST: u8 = packet::constants::RTN_UNICAST; + const BOOT_PROT: u8 = packet::constants::RTPROT_BOOT; + + let scope = route.scope as u8; + + use packet::nlas::route::Nla; + + // `rtnetlink` offers a separate request builders for different IP versions (IP v4 and v6). + // This if branch is a bit clumsy because it does almost the same. + // TODO: Simplify this once https://github.com/little-dude/netlink/pull/140 is merged and released + if is_v6 { + let dest_addr = if !route.dest.is_empty() { + Ipv6Network::from_str(&route.dest)? + } else { + Ipv6Network::new(Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 0), 0)? + }; + + // Build IP v6 request + let mut request = self + .handle + .route() + .add_v6() + .table(MAIN_TABLE) + .kind(UNICAST) + .protocol(BOOT_PROT) + .scope(scope) + .destination_prefix(dest_addr.ip(), dest_addr.prefix()) + .output_interface(link.index()); + + if !route.source.is_empty() { + let network = Ipv6Network::from_str(&route.source)?; + if network.prefix() > 0 { + request = request.source_prefix(network.ip(), network.prefix()); + } else { + request + .message_mut() + .nlas + .push(Nla::PrefSource(network.ip().octets().to_vec())); + } + } + + if !route.gateway.is_empty() { + let ip = Ipv6Addr::from_str(&route.gateway)?; + request = request.gateway(ip); + } + + request.execute().await.with_context(|| { + format!( + "Failed to add IP v6 route (src: {}, dst: {}, gtw: {})", + route.get_source(), + route.get_dest(), + route.get_gateway() + ) + })?; + } else { + let dest_addr = if !route.dest.is_empty() { + Ipv4Network::from_str(&route.dest)? + } else { + Ipv4Network::new(Ipv4Addr::new(0, 0, 0, 0), 0)? + }; + + // Build IP v4 request + let mut request = self + .handle + .route() + .add_v4() + .table(MAIN_TABLE) + .kind(UNICAST) + .protocol(BOOT_PROT) + .scope(scope) + .destination_prefix(dest_addr.ip(), dest_addr.prefix()) + .output_interface(link.index()); + + if !route.source.is_empty() { + let network = Ipv4Network::from_str(&route.source)?; + if network.prefix() > 0 { + request = request.source_prefix(network.ip(), network.prefix()); + } else { + request + .message_mut() + .nlas + .push(Nla::PrefSource(network.ip().octets().to_vec())); + } + } + + if !route.gateway.is_empty() { + let ip = Ipv4Addr::from_str(&route.gateway)?; + request = request.gateway(ip); + } + + request.execute().await?; + } + } + + Ok(()) + } + + async fn delete_routes(&mut self, routes: I) -> Result<()> + where + I: IntoIterator, + { + for route in routes.into_iter() { + if route.header.protocol == packet::constants::RTPROT_KERNEL { + continue; + } + + let index = if let Some(index) = route.output_interface() { + index + } else { + continue; + }; + + let link = self.find_link(LinkFilter::Index(index)).await?; + + let name = link.name(); + if name.contains("lo") || name.contains("::1") { + continue; + } + + self.handle.route().del(route).execute().await?; + } + + Ok(()) + } + + async fn list_addresses(&self, filter: F) -> Result> + where + F: Into>, + { + let mut request = self.handle.address().get(); + + if let Some(filter) = filter.into() { + request = match filter { + AddressFilter::LinkIndex(index) => request.set_link_index_filter(index), + AddressFilter::IpAddress(addr) => request.set_address_filter(addr), + }; + }; + + let list = request + .execute() + .try_filter_map(|msg| future::ready(Ok(Some(Address(msg))))) // Map message to `Address` + .try_collect() + .await?; + Ok(list) + } + + async fn add_addresses(&mut self, index: u32, list: I) -> Result<()> + where + I: IntoIterator, + { + for net in list.into_iter() { + self.handle + .address() + .add(index, net.ip(), net.prefix()) + .execute() + .await + .map_err(|err| anyhow!("Failed to add address {}: {:?}", net.ip(), err))?; + } + + Ok(()) + } + + async fn delete_addresses(&mut self, list: I) -> Result<()> + where + I: IntoIterator, + { + for addr in list.into_iter() { + self.handle.address().del(addr.0).execute().await?; + } + + Ok(()) + } + + pub async fn add_arp_neighbors(&mut self, list: I) -> Result<()> + where + I: IntoIterator, + { + for neigh in list.into_iter() { + self.add_arp_neighbor(&neigh).await.map_err(|err| { + anyhow!( + "Failed to add ARP neighbor {}: {:?}", + neigh.get_toIPAddress().get_address(), + err + ) + })?; + } + + Ok(()) + } + + /// Adds an ARP neighbor. + /// TODO: `rtnetlink` has no neighbours API, remove this after https://github.com/little-dude/netlink/pull/135 + async fn add_arp_neighbor(&mut self, neigh: &ARPNeighbor) -> Result<()> { + let ip_address = neigh + .toIPAddress + .as_ref() + .map(|to| to.address.as_str()) // Extract address field + .and_then(|addr| if addr.is_empty() { None } else { Some(addr) }) // Make sure it's not empty + .ok_or_else(|| nix::Error::Sys(nix::errno::Errno::EINVAL))?; + + let ip = IpAddr::from_str(&ip_address) + .map_err(|e| anyhow!("Failed to parse IP {}: {:?}", ip_address, e))?; + + // Import rtnetlink objects that make sense only for this function + use packet::constants::{NDA_UNSPEC, NLM_F_ACK, NLM_F_CREATE, NLM_F_EXCL, NLM_F_REQUEST}; + use packet::neighbour::{NeighbourHeader, NeighbourMessage}; + use packet::nlas::neighbour::Nla; + use packet::{NetlinkMessage, NetlinkPayload, RtnlMessage}; + use rtnetlink::Error; + + const IFA_F_PERMANENT: u16 = 0x80; // See https://github.com/little-dude/netlink/blob/0185b2952505e271805902bf175fee6ea86c42b8/netlink-packet-route/src/rtnl/constants.rs#L770 + + let link = self.find_link(LinkFilter::Name(&neigh.device)).await?; + + let message = NeighbourMessage { + header: NeighbourHeader { + family: match ip { + IpAddr::V4(_) => packet::AF_INET, + IpAddr::V6(_) => packet::AF_INET6, + } as u8, + ifindex: link.index(), + state: if neigh.state != 0 { + neigh.state as u16 + } else { + IFA_F_PERMANENT + }, + flags: neigh.flags as u8, + ntype: NDA_UNSPEC as u8, + }, + nlas: { + let mut nlas = vec![]; + + nlas.push(Nla::Destination(match ip { + IpAddr::V4(v4) => v4.octets().to_vec(), + IpAddr::V6(v6) => v6.octets().to_vec(), + })); + + if !neigh.lladdr.is_empty() { + nlas.push(Nla::LinkLocalAddress( + parse_mac_address(&neigh.lladdr)?.to_vec(), + )); + } + + nlas + }, + }; + + // Send request and ACK + let mut req = NetlinkMessage::from(RtnlMessage::NewNeighbour(message)); + req.header.flags = NLM_F_REQUEST | NLM_F_ACK | NLM_F_EXCL | NLM_F_CREATE; + + let mut response = self.handle.request(req)?; + while let Some(message) = response.next().await { + if let NetlinkPayload::Error(err) = message.payload { + return Err(anyhow!(Error::NetlinkError(err))); + } + } + + Ok(()) + } +} + +fn format_address(data: &[u8]) -> Result { + match data.len() { + 4 => { + // IP v4 + Ok(format!("{}.{}.{}.{}", data[0], data[1], data[2], data[3])) + } + 6 => { + // Mac address + Ok(format!( + "{:0>2X}:{:0>2X}:{:0>2X}:{:0>2X}:{:0>2X}:{:0>2X}", + data[0], data[1], data[2], data[3], data[4], data[5] + )) + } + 16 => { + // IP v6 + let octets = <[u8; 16]>::try_from(data)?; + Ok(Ipv6Addr::from(octets).to_string()) + } + _ => Err(anyhow!("Unsupported address length: {}", data.len())), + } +} + +fn is_ipv6(str: &str) -> bool { + Ipv6Addr::from_str(str).is_ok() +} + +fn parse_mac_address(addr: &str) -> Result<[u8; 6]> { + let mut split = addr.splitn(6, ':'); + + // Parse single Mac address block + let mut parse_next = || -> Result { + let v = u8::from_str_radix( + split + .next() + .ok_or_else(|| nix::Error::Sys(nix::errno::Errno::EINVAL))?, + 16, + )?; + Ok(v) + }; + + // Parse all 6 blocks + let arr = [ + parse_next()?, + parse_next()?, + parse_next()?, + parse_next()?, + parse_next()?, + parse_next()?, + ]; + + Ok(arr) +} + +/// Wraps external type with the local one, so we can implement various extensions and type conversions. +struct Link(packet::LinkMessage); + +impl Link { + /// If name. + fn name(&self) -> String { + use packet::nlas::link::Nla; + self.nlas + .iter() + .find_map(|n| { + if let Nla::IfName(name) = n { + Some(name.clone()) + } else { + None + } + }) + .unwrap_or_default() + } + + /// Extract Mac address. + fn address(&self) -> String { + use packet::nlas::link::Nla; + self.nlas + .iter() + .find_map(|n| { + if let Nla::Address(data) = n { + format_address(data).ok() + } else { + None + } + }) + .unwrap_or_default() + } + + /// Returns whether the link is UP + fn is_up(&self) -> bool { + self.header.flags & packet::rtnl::constants::IFF_UP > 0 + } + + fn index(&self) -> u32 { + self.header.index + } + + fn mtu(&self) -> Option { + use packet::nlas::link::Nla; + self.nlas.iter().find_map(|n| { + if let Nla::Mtu(mtu) = n { + Some(*mtu as u64) + } else { + None + } + }) + } +} + +impl From for Link { + fn from(msg: packet::LinkMessage) -> Self { + Link(msg) + } +} + +impl Deref for Link { + type Target = packet::LinkMessage; + + fn deref(&self) -> &Self::Target { + &self.0 + } +} + +struct Address(packet::AddressMessage); + +impl TryFrom
for IPAddress { + type Error = anyhow::Error; + + fn try_from(value: Address) -> Result { + let family = if value.is_ipv6() { + IPFamily::v4 + } else { + IPFamily::v6 + }; + + let mut address = value.address(); + if address.is_empty() { + address = value.local(); + } + + let mask = format!("{}", value.0.header.prefix_len); + + Ok(IPAddress { + family, + address, + mask, + ..Default::default() + }) + } +} + +impl Address { + fn is_ipv6(&self) -> bool { + self.0.header.family == packet::constants::AF_INET6 as u8 + } + + fn prefix(&self) -> u8 { + self.0.header.prefix_len + } + + fn address(&self) -> String { + use packet::nlas::address::Nla; + self.0 + .nlas + .iter() + .find_map(|n| { + if let Nla::Address(data) = n { + format_address(data).ok() + } else { + None + } + }) + .unwrap_or_default() + } + + fn local(&self) -> String { + use packet::nlas::address::Nla; + self.0 + .nlas + .iter() + .find_map(|n| { + if let Nla::Local(data) = n { + format_address(data).ok() + } else { + None + } + }) + .unwrap_or_default() + } +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::skip_if_not_root; + use rtnetlink::packet; + use std::iter; + use std::process::Command; + + #[tokio::test] + async fn find_link_by_name() { + let message = Handle::new() + .expect("Failed to create netlink handle") + .find_link(LinkFilter::Name("lo")) + .await + .expect("Loopback not found"); + + assert_ne!(message.header, packet::LinkHeader::default()); + assert_eq!(message.name(), "lo"); + } + + #[tokio::test] + async fn find_link_by_addr() { + let handle = Handle::new().unwrap(); + + let list = handle.list_links().await.unwrap(); + let link = list.first().expect("At least one link required"); + + let result = handle + .find_link(LinkFilter::Address(&link.address())) + .await + .expect("Failed to query link by address"); + + assert_eq!(result.header.index, link.header.index); + } + + #[tokio::test] + async fn link_up() { + skip_if_not_root!(); + + let handle = Handle::new().unwrap(); + let link = handle.find_link(LinkFilter::Name("lo")).await.unwrap(); + + handle + .enable_link(link.header.index, true) + .await + .expect("Failed to bring link up"); + + assert!(handle + .find_link(LinkFilter::Name("lo")) + .await + .unwrap() + .is_up()); + } + + #[tokio::test] + async fn link_ext() { + let lo = Handle::new() + .unwrap() + .find_link(LinkFilter::Name("lo")) + .await + .unwrap(); + + assert_eq!(lo.name(), "lo"); + assert_ne!(lo.address().len(), 0); + } + + #[tokio::test] + async fn list_routes() { + let all = Handle::new() + .unwrap() + .list_routes() + .await + .expect("Failed to list routes"); + + assert_ne!(all.len(), 0); + + for r in &all { + assert_ne!(r.device.len(), 0); + } + } + + #[tokio::test] + async fn list_addresses() { + let list = Handle::new() + .unwrap() + .list_addresses(None) + .await + .expect("Failed to list addresses"); + + assert_ne!(list.len(), 0); + for addr in &list { + assert_ne!(addr.0.header, packet::AddressHeader::default()); + } + } + + #[tokio::test] + async fn list_interfaces() { + let list = Handle::new() + .unwrap() + .list_interfaces() + .await + .expect("Failed to list interfaces"); + + for iface in &list { + assert_ne!(iface.name.len(), 0); + assert_ne!(iface.hwAddr.len(), 0); + assert_ne!(iface.mtu, 0); + + for ip in &iface.IPAddresses { + assert_ne!(ip.mask.len(), 0); + assert_ne!(ip.address.len(), 0); + } + } + } + + #[tokio::test] + async fn add_delete_addresses() { + skip_if_not_root!(); + + let list = vec![ + IpNetwork::from_str("169.254.1.1/31").unwrap(), + IpNetwork::from_str("2001:db8:85a3::8a2e:370:7334/128").unwrap(), + ]; + + let mut handle = Handle::new().unwrap(); + let lo = handle.find_link(LinkFilter::Name("lo")).await.unwrap(); + + for network in list { + handle + .add_addresses(lo.index(), iter::once(network)) + .await + .expect("Failed to add IP"); + + // Make sure the address is there + let result = handle + .list_addresses(AddressFilter::LinkIndex(lo.index())) + .await + .unwrap() + .into_iter() + .find(|p| { + p.prefix() == network.prefix() && p.address() == network.ip().to_string() + }); + + assert!(result.is_some()); + + // Delete it + handle + .delete_addresses(iter::once(result.unwrap())) + .await + .expect("Failed to delete address"); + } + } + + #[test] + fn format_addr() { + let buf = [1u8, 2u8, 3u8, 4u8]; + let addr = format_address(&buf).unwrap(); + assert_eq!(addr, "1.2.3.4"); + + let buf = [1u8, 2u8, 3u8, 4u8, 5u8, 10u8]; + let addr = format_address(&buf).unwrap(); + assert_eq!(addr, "01:02:03:04:05:0A"); + } + + #[test] + fn parse_mac() { + let bytes = parse_mac_address("AB:0C:DE:12:34:56").expect("Failed to parse mac address"); + assert_eq!(bytes, [0xAB, 0x0C, 0xDE, 0x12, 0x34, 0x56]); + } + + #[test] + fn check_ipv6() { + assert!(is_ipv6("::1")); + assert!(is_ipv6("2001:0:3238:DFE1:63::FEFB")); + + assert!(!is_ipv6("")); + assert!(!is_ipv6("127.0.0.1")); + assert!(!is_ipv6("10.10.10.10")); + } + + fn clean_env_for_test_add_one_arp_neighbor(dummy_name: &str, ip: &str) { + // ip link delete dummy + Command::new("ip") + .args(&["link", "delete", dummy_name]) + .output() + .expect("prepare: failed to delete dummy"); + + // ip neigh del dev dummy ip + Command::new("ip") + .args(&["neigh", "del", dummy_name, ip]) + .output() + .expect("prepare: failed to delete neigh"); + } + + fn prepare_env_for_test_add_one_arp_neighbor(dummy_name: &str, ip: &str) { + clean_env_for_test_add_one_arp_neighbor(dummy_name, ip); + // modprobe dummy + Command::new("modprobe") + .arg("dummy") + .output() + .expect("failed to run modprobe dummy"); + + // ip link add dummy type dummy + Command::new("ip") + .args(&["link", "add", dummy_name, "type", "dummy"]) + .output() + .expect("failed to add dummy interface"); + + // ip addr add 192.168.0.2/16 dev dummy + Command::new("ip") + .args(&["addr", "add", "192.168.0.2/16", "dev", dummy_name]) + .output() + .expect("failed to add ip for dummy"); + + // ip link set dummy up; + Command::new("ip") + .args(&["link", "set", dummy_name, "up"]) + .output() + .expect("failed to up dummy"); + } + + #[tokio::test] + async fn test_add_one_arp_neighbor() { + skip_if_not_root!(); + + let mac = "6a:92:3a:59:70:aa"; + let to_ip = "169.254.1.1"; + let dummy_name = "dummy_for_arp"; + + prepare_env_for_test_add_one_arp_neighbor(dummy_name, to_ip); + + let mut ip_address = IPAddress::new(); + ip_address.set_address(to_ip.to_string()); + + let mut neigh = ARPNeighbor::new(); + neigh.set_toIPAddress(ip_address); + neigh.set_device(dummy_name.to_string()); + neigh.set_lladdr(mac.to_string()); + neigh.set_state(0x80); + + Handle::new() + .unwrap() + .add_arp_neighbor(&neigh) + .await + .expect("Failed to add ARP neighbor"); + + // ip neigh show dev dummy ip + let stdout = Command::new("ip") + .args(&["neigh", "show", "dev", dummy_name, to_ip]) + .output() + .expect("failed to show neigh") + .stdout; + + let stdout = std::str::from_utf8(&stdout).expect("failed to conveert stdout"); + assert_eq!(stdout, format!("{} lladdr {} PERMANENT\n", to_ip, mac)); + + clean_env_for_test_add_one_arp_neighbor(dummy_name, to_ip); + } +} diff --git a/src/agent/src/rpc.rs b/src/agent/src/rpc.rs index 7d9a657ade..4a1363d73e 100644 --- a/src/agent/src/rpc.rs +++ b/src/agent/src/rpc.rs @@ -51,7 +51,6 @@ use crate::random; use crate::sandbox::Sandbox; use crate::version::{AGENT_VERSION, API_VERSION}; use crate::AGENT_CONFIG; -use netlink::{RtnlHandle, NETLINK_ROUTE}; use libc::{self, c_ushort, pid_t, winsize, TIOCSWINSZ}; use std::convert::TryFrom; @@ -850,30 +849,24 @@ impl protocols::agent_ttrpc::AgentService for agentService { _ctx: &TtrpcContext, req: protocols::agent::UpdateInterfaceRequest, ) -> ttrpc::Result { - if req.interface.is_none() { - return Err(ttrpc_error( + let interface = req.interface.into_option().ok_or_else(|| { + ttrpc_error( ttrpc::Code::INVALID_ARGUMENT, "empty update interface request".to_string(), - )); - } + ) + })?; - let interface = req.interface; - let s = Arc::clone(&self.sandbox); - let mut sandbox = s.lock().await; - - if sandbox.rtnl.is_none() { - sandbox.rtnl = Some(RtnlHandle::new(NETLINK_ROUTE, 0).unwrap()); - } - - let rtnl = sandbox.rtnl.as_mut().unwrap(); - - let iface = rtnl - .update_interface(interface.as_ref().unwrap()) + self.sandbox + .lock() + .await + .rtnl + .update_interface(&interface) + .await .map_err(|e| { ttrpc_error(ttrpc::Code::INTERNAL, format!("update interface: {:?}", e)) })?; - Ok(iface) + Ok(interface) } async fn update_routes( @@ -881,38 +874,37 @@ impl protocols::agent_ttrpc::AgentService for agentService { _ctx: &TtrpcContext, req: protocols::agent::UpdateRoutesRequest, ) -> ttrpc::Result { - let mut routes = protocols::agent::Routes::new(); - if req.routes.is_none() { - return Err(ttrpc_error( - ttrpc::Code::INVALID_ARGUMENT, - "empty update routes request".to_string(), - )); - } + let new_routes = req + .routes + .into_option() + .map(|r| r.Routes.into_vec()) + .ok_or_else(|| { + ttrpc_error( + ttrpc::Code::INVALID_ARGUMENT, + "empty update routes request".to_string(), + ) + })?; - let rs = req.routes.unwrap().Routes.into_vec(); + let mut sandbox = self.sandbox.lock().await; - let s = Arc::clone(&self.sandbox); - let mut sandbox = s.lock().await; + sandbox.rtnl.update_routes(new_routes).await.map_err(|e| { + ttrpc_error( + ttrpc::Code::INTERNAL, + format!("Failed to update routes: {:?}", e), + ) + })?; - if sandbox.rtnl.is_none() { - sandbox.rtnl = Some(RtnlHandle::new(NETLINK_ROUTE, 0).unwrap()); - } + let list = sandbox.rtnl.list_routes().await.map_err(|e| { + ttrpc_error( + ttrpc::Code::INTERNAL, + format!("Failed to list routes after update: {:?}", e), + ) + })?; - let rtnl = sandbox.rtnl.as_mut().unwrap(); - - // get current routes to return when error out - let crs = rtnl - .list_routes() - .map_err(|e| ttrpc_error(ttrpc::Code::INTERNAL, format!("update routes: {:?}", e)))?; - - let v = match rtnl.update_routes(rs.as_ref()) { - Ok(value) => value, - Err(_) => crs, - }; - - routes.set_Routes(RepeatedField::from_vec(v)); - - Ok(routes) + Ok(protocols::agent::Routes { + Routes: RepeatedField::from_vec(list), + ..Default::default() + }) } async fn list_interfaces( @@ -920,22 +912,24 @@ impl protocols::agent_ttrpc::AgentService for agentService { _ctx: &TtrpcContext, _req: protocols::agent::ListInterfacesRequest, ) -> ttrpc::Result { - let mut interface = protocols::agent::Interfaces::new(); - let s = Arc::clone(&self.sandbox); - let mut sandbox = s.lock().await; - - if sandbox.rtnl.is_none() { - sandbox.rtnl = Some(RtnlHandle::new(NETLINK_ROUTE, 0).unwrap()); - } - - let rtnl = sandbox.rtnl.as_mut().unwrap(); - let v = rtnl + let list = self + .sandbox + .lock() + .await + .rtnl .list_interfaces() - .map_err(|e| ttrpc_error(ttrpc::Code::INTERNAL, format!("list interface: {:?}", e)))?; + .await + .map_err(|e| { + ttrpc_error( + ttrpc::Code::INTERNAL, + format!("Failed to list interfaces: {:?}", e), + ) + })?; - interface.set_Interfaces(RepeatedField::from_vec(v)); - - Ok(interface) + Ok(protocols::agent::Interfaces { + Interfaces: RepeatedField::from_vec(list), + ..Default::default() + }) } async fn list_routes( @@ -943,23 +937,19 @@ impl protocols::agent_ttrpc::AgentService for agentService { _ctx: &TtrpcContext, _req: protocols::agent::ListRoutesRequest, ) -> ttrpc::Result { - let mut routes = protocols::agent::Routes::new(); - let s = Arc::clone(&self.sandbox); - let mut sandbox = s.lock().await; - - if sandbox.rtnl.is_none() { - sandbox.rtnl = Some(RtnlHandle::new(NETLINK_ROUTE, 0).unwrap()); - } - - let rtnl = sandbox.rtnl.as_mut().unwrap(); - - let v = rtnl + let list = self + .sandbox + .lock() + .await + .rtnl .list_routes() + .await .map_err(|e| ttrpc_error(ttrpc::Code::INTERNAL, format!("list routes: {:?}", e)))?; - routes.set_Routes(RepeatedField::from_vec(v)); - - Ok(routes) + Ok(protocols::agent::Routes { + Routes: RepeatedField::from_vec(list), + ..Default::default() + }) } async fn start_tracing( @@ -1062,26 +1052,29 @@ impl protocols::agent_ttrpc::AgentService for agentService { _ctx: &TtrpcContext, req: protocols::agent::AddARPNeighborsRequest, ) -> ttrpc::Result { - if req.neighbors.is_none() { - return Err(ttrpc_error( - ttrpc::Code::INVALID_ARGUMENT, - "empty add arp neighbours request".to_string(), - )); - } + let neighs = req + .neighbors + .into_option() + .map(|n| n.ARPNeighbors.into_vec()) + .ok_or_else(|| { + ttrpc_error( + ttrpc::Code::INVALID_ARGUMENT, + "empty add arp neighbours request".to_string(), + ) + })?; - let neighs = req.neighbors.unwrap().ARPNeighbors.into_vec(); - - let s = Arc::clone(&self.sandbox); - let mut sandbox = s.lock().await; - - if sandbox.rtnl.is_none() { - sandbox.rtnl = Some(RtnlHandle::new(NETLINK_ROUTE, 0).unwrap()); - } - - let rtnl = sandbox.rtnl.as_mut().unwrap(); - - rtnl.add_arp_neighbors(neighs.as_ref()) - .map_err(|e| ttrpc_error(ttrpc::Code::INTERNAL, e.to_string()))?; + self.sandbox + .lock() + .await + .rtnl + .add_arp_neighbors(neighs) + .await + .map_err(|e| { + ttrpc_error( + ttrpc::Code::INTERNAL, + format!("Failed to add ARP neighbours: {:?}", e), + ) + })?; Ok(Empty::new()) } @@ -1709,8 +1702,8 @@ mod tests { assert!(result.is_ok(), "load module should success"); } - #[test] - fn test_append_guest_hooks() { + #[tokio::test] + async fn test_append_guest_hooks() { let logger = slog::Logger::root(slog::Discard, o!()); let mut s = Sandbox::new(&logger).unwrap(); s.hooks = Some(Hooks { diff --git a/src/agent/src/sandbox.rs b/src/agent/src/sandbox.rs index a02ec946e2..10022c0458 100644 --- a/src/agent/src/sandbox.rs +++ b/src/agent/src/sandbox.rs @@ -6,10 +6,10 @@ use crate::linux_abi::*; use crate::mount::{get_mount_fs_type, remove_mounts, TYPE_ROOTFS}; use crate::namespace::Namespace; +use crate::netlink::Handle; use crate::network::Network; use anyhow::{anyhow, Context, Result}; use libc::pid_t; -use netlink::{RtnlHandle, NETLINK_ROUTE}; use oci::{Hook, Hooks}; use protocols::agent::OnlineCPUMemRequest; use regex::Regex; @@ -44,7 +44,7 @@ pub struct Sandbox { pub running: bool, pub no_pivot_root: bool, pub sender: Option>, - pub rtnl: Option, + pub rtnl: Handle, pub hooks: Option, pub event_rx: Arc>>, pub event_tx: Sender, @@ -73,7 +73,7 @@ impl Sandbox { running: false, no_pivot_root: fs_type.eq(TYPE_ROOTFS), sender: None, - rtnl: Some(RtnlHandle::new(NETLINK_ROUTE, 0).unwrap()), + rtnl: Handle::new()?, hooks: None, event_rx, event_tx: tx, @@ -433,8 +433,8 @@ mod tests { baremount.mount() } - #[test] - fn set_sandbox_storage() { + #[tokio::test] + async fn set_sandbox_storage() { let logger = slog::Logger::root(slog::Discard, o!()); let mut s = Sandbox::new(&logger).unwrap(); @@ -467,8 +467,8 @@ mod tests { ); } - #[test] - fn remove_sandbox_storage() { + #[tokio::test] + async fn remove_sandbox_storage() { skip_if_not_root!(); let logger = slog::Logger::root(slog::Discard, o!()); @@ -523,9 +523,9 @@ mod tests { assert!(s.remove_sandbox_storage(destdir_path).is_ok()); } - #[test] + #[tokio::test] #[allow(unused_assignments)] - fn unset_and_remove_sandbox_storage() { + async fn unset_and_remove_sandbox_storage() { skip_if_not_root!(); let logger = slog::Logger::root(slog::Discard, o!()); @@ -575,8 +575,8 @@ mod tests { assert!(s.unset_and_remove_sandbox_storage(&other_dir_str).is_err()); } - #[test] - fn unset_sandbox_storage() { + #[tokio::test] + async fn unset_sandbox_storage() { let logger = slog::Logger::root(slog::Discard, o!()); let mut s = Sandbox::new(&logger).unwrap(); @@ -658,8 +658,8 @@ mod tests { .unwrap() } - #[test] - fn get_container_entry_exist() { + #[tokio::test] + async fn get_container_entry_exist() { skip_if_not_root!(); let logger = slog::Logger::root(slog::Discard, o!()); let mut s = Sandbox::new(&logger).unwrap(); @@ -671,8 +671,8 @@ mod tests { assert!(cnt.is_some()); } - #[test] - fn get_container_no_entry() { + #[tokio::test] + async fn get_container_no_entry() { let logger = slog::Logger::root(slog::Discard, o!()); let mut s = Sandbox::new(&logger).unwrap(); @@ -680,8 +680,8 @@ mod tests { assert!(cnt.is_none()); } - #[test] - fn add_and_get_container() { + #[tokio::test] + async fn add_and_get_container() { skip_if_not_root!(); let logger = slog::Logger::root(slog::Discard, o!()); let mut s = Sandbox::new(&logger).unwrap(); @@ -690,8 +690,9 @@ mod tests { s.add_container(linux_container); assert!(s.get_container("some_id").is_some()); } - #[test] - fn update_shared_pidns() { + + #[tokio::test] + async fn update_shared_pidns() { skip_if_not_root!(); let logger = slog::Logger::root(slog::Discard, o!()); let mut s = Sandbox::new(&logger).unwrap(); @@ -707,8 +708,9 @@ mod tests { let ns_path = format!("/proc/{}/ns/pid", test_pid); assert_eq!(s.sandbox_pidns.unwrap().path, ns_path); } - #[test] - fn add_guest_hooks() { + + #[tokio::test] + async fn add_guest_hooks() { let logger = slog::Logger::root(slog::Discard, o!()); let mut s = Sandbox::new(&logger).unwrap(); let tmpdir = Builder::new().tempdir().unwrap(); @@ -730,8 +732,8 @@ mod tests { assert!(s.hooks.as_ref().unwrap().poststop.is_empty()); } - #[test] - pub fn test_sandbox_is_running() { + #[tokio::test] + async fn test_sandbox_is_running() { let logger = slog::Logger::root(slog::Discard, o!()); let mut s = Sandbox::new(&logger).unwrap(); s.running = true; @@ -740,8 +742,8 @@ mod tests { assert!(!s.is_running()); } - #[test] - fn test_sandbox_set_hostname() { + #[tokio::test] + async fn test_sandbox_set_hostname() { let logger = slog::Logger::root(slog::Discard, o!()); let mut s = Sandbox::new(&logger).unwrap(); let hostname = "abc123";