libslirp-4.3.0/.cargo_vcs_info.json0000644000000001121374456736300127320ustar { "git": { "sha1": "191da4e0a9148a883771e1bd390c8c78ca39e4a6" } } libslirp-4.3.0/.gitlab-ci.yml010064400017500001750000000006551374401444000141620ustar 00000000000000image: "rust:latest" before_script: - apt-get update -yqq - apt-get install -yqq --no-install-recommends build-essential git meson dbus libdbus-1-dev python3-scapy python3-pydbus - git clone https://gitlab.freedesktop.org/slirp/libslirp.git libslirp - (cd libslirp && meson --prefix=/usr build && ninja -C build install) test:cargo: script: - rustc --version && cargo --version - cargo test --all --verbose - make test libslirp-4.3.0/Cargo.lock0000644000000471711374456736300107250ustar # This file is automatically @generated by Cargo. # It is not intended for manual editing. [[package]] name = "ansi_term" version = "0.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ee49baf6cb617b853aa8d93bf420db2383fab46d314482ca2803b40d5fde979b" dependencies = [ "winapi 0.3.9", ] [[package]] name = "atty" version = "0.2.14" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8" dependencies = [ "hermit-abi", "libc", "winapi 0.3.9", ] [[package]] name = "bitflags" version = "1.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cf1de2fe8c75bc145a2f577add951f8134889b4795d47466a54a5c846d691693" [[package]] name = "block-buffer" version = "0.7.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c0940dc441f31689269e10ac70eb1002a3a1d3ad1390e030043662eb7fe4688b" dependencies = [ "block-padding", "byte-tools", "byteorder", "generic-array", ] [[package]] name = "block-padding" version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fa79dedbb091f449f1f39e53edf88d5dbe95f895dae6135a8d7b881fb5af73f5" dependencies = [ "byte-tools", ] [[package]] name = "byte-tools" version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e3b5ca7a04898ad4bcd41c90c5285445ff5b791899bb1b0abdd2a2aa791211d7" [[package]] name = "byteorder" version = "1.3.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "08c48aae112d48ed9f069b33538ea9e3e90aa263cfa3d1c24309612b1f7472de" [[package]] name = "cc" version = "1.0.61" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ed67cbde08356238e75fc4656be4749481eeffb09e19f320a25237d5221c985d" [[package]] name = "cfg-if" version = "0.1.10" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4785bdd1c96b2a846b2bd7cc02e86b6b3dbf14e7e53446c4f54c92a361040822" [[package]] name = "clap" version = "2.33.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "37e58ac78573c40708d45522f0d80fa2f01cc4f9b4e2bf749807255454312002" dependencies = [ "ansi_term", "atty", "bitflags", "strsim", "textwrap", "unicode-width", "vec_map", ] [[package]] name = "crypto-mac" version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4434400df11d95d556bac068ddfedd482915eb18fe8bea89bc80b6e4b1c179e5" dependencies = [ "generic-array", "subtle", ] [[package]] name = "derivative" version = "2.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cb582b60359da160a9477ee80f15c8d784c477e69c217ef2cdd4169c24ea380f" dependencies = [ "proc-macro2", "quote", "syn", ] [[package]] name = "digest" version = "0.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f3d0c8c8752312f9713efd397ff63acb9f85585afbf179282e720e7704954dd5" dependencies = [ "generic-array", ] [[package]] name = "enumflags2" version = "0.6.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "83c8d82922337cd23a15f88b70d8e4ef5f11da38dd7cdb55e84dd5de99695da0" dependencies = [ "enumflags2_derive", "serde", ] [[package]] name = "enumflags2_derive" version = "0.6.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "946ee94e3dbf58fdd324f9ce245c7b238d46a66f00e86a020b71996349e46cce" dependencies = [ "proc-macro2", "quote", "syn", ] [[package]] name = "error-chain" version = "0.12.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2d2f06b9cac1506ece98fe3231e3cc9c4410ec3d5b1f24ae1c8946f0742cdefc" dependencies = [ "version_check", ] [[package]] name = "etherparse" version = "0.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "706c7cc3e05a64c496e9cca52ef8ad82a846a8c5b03345fddd81a76009a435b7" dependencies = [ "byteorder", ] [[package]] name = "fake-simd" version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e88a8acf291dafb59c2d96e8f59828f3838bb1a70398823ade51a84de6a6deed" [[package]] name = "fastrand" version = "1.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ca5faf057445ce5c9d4329e382b2ce7ca38550ef3b73a5348362d5f24e0c7fe3" dependencies = [ "instant", ] [[package]] name = "fuchsia-zircon" version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2e9763c69ebaae630ba35f74888db465e49e259ba1bc0eda7d06f4a067615d82" dependencies = [ "bitflags", "fuchsia-zircon-sys", ] [[package]] name = "fuchsia-zircon-sys" version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3dcaa9ae7725d12cdb85b3ad99a434db70b468c09ded17e012d86b5c1010f7a7" [[package]] name = "generic-array" version = "0.12.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c68f0274ae0e023facc3c97b2e00f076be70e254bc851d972503b328db79b2ec" dependencies = [ "typenum", ] [[package]] name = "heck" version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "20564e78d53d2bb135c343b3f47714a56af2061f1c928fdb541dc7b9fdd94205" dependencies = [ "unicode-segmentation", ] [[package]] name = "hermit-abi" version = "0.1.17" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5aca5565f760fb5b220e499d72710ed156fdb74e631659e99377d9ebfbd13ae8" dependencies = [ "libc", ] [[package]] name = "hmac" version = "0.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5dcb5e64cda4c23119ab41ba960d1e170a774c8e4b9d9e6a9bc18aabf5e59695" dependencies = [ "crypto-mac", "digest", ] [[package]] name = "idna" version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "02e2673c30ee86b5b96a9cb52ad15718aa1f966f5ab9ad54a8b95d5ca33120a9" dependencies = [ "matches", "unicode-bidi", "unicode-normalization", ] [[package]] name = "instant" version = "0.1.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "63312a18f7ea8760cdd0a7c5aac1a619752a246b833545e3e36d1f81f7cd9e66" dependencies = [ "cfg-if", ] [[package]] name = "iovec" version = "0.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b2b3ea6ff95e175473f8ffe6a7eb7c00d054240321b84c57051175fe3c1e075e" 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 = "kernel32-sys" version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7507624b29483431c0ba2d82aece8ca6cdba9382bff4ddd0f7490560c056098d" dependencies = [ "winapi 0.2.8", "winapi-build", ] [[package]] name = "lazy_static" version = "1.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" [[package]] name = "lazycell" version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "830d08ce1d1d941e6b30645f1a0eb5643013d835ce3779a5fc208261dbe10f55" [[package]] name = "libc" version = "0.2.79" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2448f6066e80e3bfc792e9c98bf705b4b0fc6e8ef5b43e5889aff0eaa9c58743" [[package]] name = "libslirp" version = "4.3.0" dependencies = [ "enumflags2", "etherparse", "ipnetwork", "lazy_static", "libc", "libslirp-sys", "libsystemd", "mio", "mio-extras", "nix", "slab", "structopt", "url", "zbus", "zvariant", ] [[package]] name = "libslirp-sys" version = "4.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "26e54a5719a79bee3b25ee15e4b2cf80f51597b2d45d9889f3c1b8c150d907b4" dependencies = [ "pkg-config", ] [[package]] name = "libsystemd" version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f3a64961e79726a5b05e0db592097ca895831d755484203578fe75b580847262" dependencies = [ "error-chain", "hmac", "libc", "nix", "serde", "sha2", "uuid", ] [[package]] name = "log" version = "0.4.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4fabed175da42fed1fa0746b0ea71f412aa9d35e76e95e59b192c64b9dc2bf8b" dependencies = [ "cfg-if", ] [[package]] name = "matches" version = "0.1.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7ffc5c5338469d4d3ea17d269fa8ea3512ad247247c30bd2df69e68309ed0a08" [[package]] name = "mio" version = "0.6.22" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fce347092656428bc8eaf6201042cb551b8d67855af7374542a92a0fbfcac430" dependencies = [ "cfg-if", "fuchsia-zircon", "fuchsia-zircon-sys", "iovec", "kernel32-sys", "libc", "log", "miow", "net2", "slab", "winapi 0.2.8", ] [[package]] name = "mio-extras" version = "2.0.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "52403fe290012ce777c4626790c8951324a2b9e3316b3143779c72b029742f19" dependencies = [ "lazycell", "log", "mio", "slab", ] [[package]] name = "miow" version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8c1f2f3b1cf331de6896aabf6e9d55dca90356cc9960cca7eaaf408a355ae919" dependencies = [ "kernel32-sys", "net2", "winapi 0.2.8", "ws2_32-sys", ] [[package]] name = "net2" version = "0.2.35" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3ebc3ec692ed7c9a255596c67808dee269f64655d8baf7b4f0638e51ba1d6853" dependencies = [ "cfg-if", "libc", "winapi 0.3.9", ] [[package]] name = "nix" version = "0.17.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "50e4785f2c3b7589a0d0c1dd60285e1188adac4006e8abd6dd578e1567027363" dependencies = [ "bitflags", "cc", "cfg-if", "libc", "void", ] [[package]] name = "once_cell" version = "1.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "260e51e7efe62b592207e9e13a68e43692a7a279171d6ba57abd208bf23645ad" [[package]] name = "opaque-debug" version = "0.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2839e79665f131bdb5782e51f2c6c9599c133c6098982a54c794358bf432529c" [[package]] name = "percent-encoding" version = "2.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d4fd5641d01c8f18a23da7b6fe29298ff4b55afcccdf78973b24cf3175fee32e" [[package]] name = "pkg-config" version = "0.3.19" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3831453b3449ceb48b6d9c7ad7c96d5ea673e9b470a1dc578c2ce6521230884c" [[package]] name = "proc-macro-crate" version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1d6ea3c4595b96363c13943497db34af4460fb474a95c43f4446ad341b8c9785" dependencies = [ "toml", ] [[package]] name = "proc-macro-error" version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "da25490ff9892aab3fcf7c36f08cfb902dd3e71ca0f9f9517bea02a73a5ce38c" dependencies = [ "proc-macro-error-attr", "proc-macro2", "quote", "syn", "version_check", ] [[package]] name = "proc-macro-error-attr" version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a1be40180e52ecc98ad80b184934baf3d0d29f979574e439af5a55274b35f869" dependencies = [ "proc-macro2", "quote", "version_check", ] [[package]] name = "proc-macro2" version = "1.0.24" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1e0704ee1a7e00d7bb417d0770ea303c1bccbabf0ef1667dae92b5967f5f8a71" dependencies = [ "unicode-xid", ] [[package]] name = "quote" version = "1.0.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "aa563d17ecb180e500da1cfd2b028310ac758de548efdd203e18f283af693f37" dependencies = [ "proc-macro2", ] [[package]] name = "scoped-tls" version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ea6a9290e3c9cf0f18145ef7ffa62d68ee0bf5fcd651017e586dc7fd5da448c2" [[package]] name = "serde" version = "1.0.117" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b88fa983de7720629c9387e9f517353ed404164b1e482c970a90c1a4aaf7dc1a" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" version = "1.0.117" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cbd1ae72adb44aab48f325a02444a5fc079349a8d804c1fc922aed3f7454c74e" dependencies = [ "proc-macro2", "quote", "syn", ] [[package]] name = "serde_repr" version = "0.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2dc6b7951b17b051f3210b063f12cc17320e2fe30ae05b0fe2a3abb068551c76" dependencies = [ "proc-macro2", "quote", "syn", ] [[package]] name = "sha2" version = "0.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a256f46ea78a0c0d9ff00077504903ac881a1dafdc20da66545699e7776b3e69" dependencies = [ "block-buffer", "digest", "fake-simd", "opaque-debug", ] [[package]] name = "slab" version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c111b5bd5695e56cffe5129854aa230b39c93a305372fdbb2668ca2394eea9f8" [[package]] name = "strsim" version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8ea5119cdb4c55b55d432abb513a0429384878c15dde60cc77b1c99de1a95a6a" [[package]] name = "structopt" version = "0.3.20" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "126d630294ec449fae0b16f964e35bf3c74f940da9dca17ee9b905f7b3112eb8" dependencies = [ "clap", "lazy_static", "structopt-derive", ] [[package]] name = "structopt-derive" version = "0.4.13" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "65e51c492f9e23a220534971ff5afc14037289de430e3c83f9daf6a1b6ae91e8" dependencies = [ "heck", "proc-macro-error", "proc-macro2", "quote", "syn", ] [[package]] name = "subtle" version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2d67a5a62ba6e01cb2192ff309324cb4875d0c451d55fe2319433abe7a05a8ee" [[package]] name = "syn" version = "1.0.45" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ea9c5432ff16d6152371f808fb5a871cd67368171b09bb21b43df8e4a47a3556" dependencies = [ "proc-macro2", "quote", "unicode-xid", ] [[package]] name = "textwrap" version = "0.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d326610f408c7a4eb6f51c37c330e496b08506c9457c9d34287ecc38809fb060" dependencies = [ "unicode-width", ] [[package]] name = "tinyvec" version = "0.3.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "238ce071d267c5710f9d31451efec16c5ee22de34df17cc05e56cbc92e967117" [[package]] name = "toml" version = "0.5.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "75cf45bb0bef80604d001caaec0d09da99611b3c0fd39d3080468875cdb65645" dependencies = [ "serde", ] [[package]] name = "typenum" version = "1.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "373c8a200f9e67a0c95e62a4f52fbf80c23b4381c05a17845531982fa99e6b33" [[package]] name = "unicode-bidi" version = "0.3.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "49f2bd0c6468a8230e1db229cff8029217cf623c767ea5d60bfbd42729ea54d5" dependencies = [ "matches", ] [[package]] name = "unicode-normalization" version = "0.1.13" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6fb19cf769fa8c6a80a162df694621ebeb4dafb606470b2b2fce0be40a98a977" dependencies = [ "tinyvec", ] [[package]] name = "unicode-segmentation" version = "1.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e83e153d1053cbb5a118eeff7fd5be06ed99153f00dbcd8ae310c5fb2b22edc0" [[package]] name = "unicode-width" version = "0.1.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9337591893a19b88d8d87f2cec1e73fad5cdfd10e5a6f349f498ad6ea2ffb1e3" [[package]] name = "unicode-xid" version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f7fe0bb3479651439c9112f72b6c505038574c9fbb575ed1bf3b797fa39dd564" [[package]] name = "url" version = "2.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "829d4a8476c35c9bf0bbce5a3b23f4106f79728039b726d292bb93bc106787cb" dependencies = [ "idna", "matches", "percent-encoding", ] [[package]] name = "uuid" version = "0.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9fde2f6a4bea1d6e007c4ad38c6839fa71cbb63b6dbf5b595aa38dc9b1093c11" dependencies = [ "serde", ] [[package]] name = "vec_map" version = "0.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f1bddf1187be692e79c5ffeab891132dfb0f236ed36a43c7ed39f1165ee20191" [[package]] name = "version_check" version = "0.9.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b5a972e5669d67ba988ce3dc826706fb0a8b01471c088cb0b6110b805cc36aed" [[package]] name = "void" version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6a02e4885ed3bc0f2de90ea6dd45ebcbb66dacffe03547fadbb0eeae2770887d" [[package]] name = "winapi" version = "0.2.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "167dc9d6949a9b857f3451275e911c3f44255842c1f7a76f33c55103a909087a" [[package]] name = "winapi" version = "0.3.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" dependencies = [ "winapi-i686-pc-windows-gnu", "winapi-x86_64-pc-windows-gnu", ] [[package]] name = "winapi-build" version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2d315eee3b34aca4797b2da6b13ed88266e6d612562a0c46390af8299fc699bc" [[package]] name = "winapi-i686-pc-windows-gnu" version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" [[package]] name = "winapi-x86_64-pc-windows-gnu" version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" [[package]] name = "ws2_32-sys" version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d59cefebd0c892fa2dd6de581e937301d8552cb44489cdff035c6187cb63fa5e" dependencies = [ "winapi 0.2.8", "winapi-build", ] [[package]] name = "zbus" version = "1.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3a8a1a98eac87778308d7b2725cee6543a13936a3913b98e37fcf8ce9e4362da" dependencies = [ "byteorder", "derivative", "enumflags2", "fastrand", "nix", "once_cell", "scoped-tls", "serde", "serde_repr", "zbus_macros", "zvariant", ] [[package]] name = "zbus_macros" version = "1.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9ca4d050be67f3b7a29878a6f0788a8dcd100c7297e619dcb91051f94679a8a4" dependencies = [ "proc-macro-crate", "proc-macro2", "quote", "syn", ] [[package]] name = "zvariant" version = "2.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fb79b31a454101beda19e64082dfd9ad59c5ff8afa41e1de1065333105b02740" dependencies = [ "byteorder", "enumflags2", "serde", "zvariant_derive", ] [[package]] name = "zvariant_derive" version = "2.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "605bf414a59d93a3d55055e72ea066c5c0bc578ed2227df15c407207b1c780ef" dependencies = [ "proc-macro-crate", "proc-macro2", "quote", "syn", ] libslirp-4.3.0/Cargo.toml0000644000000042451374456736300107430ustar # THIS FILE IS AUTOMATICALLY GENERATED BY CARGO # # When uploading crates to the registry Cargo will automatically # "normalize" Cargo.toml files for maximal compatibility # with all versions of Cargo and also rewrite `path` dependencies # to registry (e.g., crates.io) dependencies # # If you believe there's an error in this file please file an # issue against the rust-lang/cargo repository. If you're # editing this file be aware that the upstream Cargo.toml # will likely look very different (and much more reasonable) [package] edition = "2018" name = "libslirp" version = "4.3.0" authors = ["Marc-André Lureau "] description = "High-level bindings & helper process for libslirp." homepage = "https://gitlab.freedesktop.org/slirp/libslirp-rs" documentation = "https://docs.rs/libslirp" keywords = ["networking", "tcp", "ip", "qemu", "virtualization"] categories = ["api-bindings", "command-line-utilities", "emulators", "network-programming"] license-file = "LICENSE" repository = "https://gitlab.freedesktop.org/slirp/libslirp-rs.git" [[bin]] name = "libslirp-helper" required-features = ["default", "helper"] [[test]] name = "test-ip" required-features = ["default"] [dependencies.enumflags2] version = "0.6.4" optional = true [dependencies.ipnetwork] version = "0.17" optional = true [dependencies.lazy_static] version = "1.4" optional = true [dependencies.libc] version = "0.2" optional = true [dependencies.libslirp-sys] version = "4.2.0" [dependencies.libsystemd] version = "0.2" optional = true [dependencies.mio] version = "0.6.19" optional = true [dependencies.mio-extras] version = "2.0.5" optional = true [dependencies.nix] version = "0.17" optional = true [dependencies.slab] version = "0.4.0" optional = true [dependencies.structopt] version = "0.3.0" optional = true [dependencies.url] version = "2.1" optional = true [dependencies.zbus] version = "1.0" optional = true [dependencies.zvariant] version = "2.0" optional = true [dev-dependencies.etherparse] version = "0.8.0" [features] default = ["mio", "mio-extras", "ipnetwork", "structopt", "slab"] helper = ["libc", "zbus", "nix", "libsystemd", "url", "lazy_static", "zvariant", "enumflags2"] libslirp-4.3.0/Cargo.toml.orig010064400017500001750000000030431374455400200144120ustar 00000000000000[package] name = "libslirp" version = "4.3.0" authors = ["Marc-André Lureau "] repository = "https://gitlab.freedesktop.org/slirp/libslirp-rs.git" homepage = "https://gitlab.freedesktop.org/slirp/libslirp-rs" documentation = "https://docs.rs/libslirp" description = "High-level bindings & helper process for libslirp." license-file = "LICENSE" edition = "2018" keywords = ["networking", "tcp", "ip", "qemu", "virtualization"] categories = ["api-bindings", "command-line-utilities", "emulators", "network-programming"] [features] default = ["mio", "mio-extras", "ipnetwork", "structopt", "slab"] helper = ["libc", "zbus", "nix", "libsystemd", "url", "lazy_static", "zvariant", "enumflags2"] [dependencies] libslirp-sys = { version = "4.2.0" } ipnetwork = { version = "0.17", optional = true } structopt = { version = "0.3.0", optional = true } mio = { version = "0.6.19", optional = true } mio-extras = { version = "2.0.5", optional = true } slab = { version = "0.4.0", optional = true } libc = { version = "0.2", optional = true } nix = { version = "0.17", optional = true } libsystemd = { version = "0.2", optional = true } url = { version = "2.1", optional = true } lazy_static = { version = "1.4", optional = true } zbus = { version = "1.0", optional = true } zvariant = { version = "2.0", optional = true } enumflags2 = { version = "0.6.4", optional = true } [dev-dependencies] etherparse = "0.8.0" [[bin]] name = "libslirp-helper" required-features = ["default", "helper"] [[test]] name = "test-ip" required-features = ["default"] libslirp-4.3.0/LICENSE010064400017500001750000000020621374401444000125250ustar 00000000000000MIT License Copyright (c) 2019-2020 Red Hat, Inc Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. libslirp-4.3.0/Makefile010064400017500001750000000005421374401444000131610ustar 00000000000000CARGO = cargo CARGOFLAGS = SLIRPHELPER = target/debug/libslirp-helper .PHONY: $(SLIRPHELPER) $(SLIRPHELPER): $(CARGO) build --all-features $(CARGOFLAGS) .PHONY: test test: $(SLIRPHELPER) SLIRPHELPER=$(SLIRPHELPER) \ PYTHONPATH=. \ PYTHONIOENCODING=utf-8 \ unshare -Ur \ dbus-run-session --config-file=tests/dbus.conf \ python3 -m unittest -v libslirp-4.3.0/src/bin/libslirp-helper/main.rs010064400017500001750000000203051374401567400174600ustar 00000000000000use std::convert::TryInto; use std::error::Error; use std::fs::File; use std::io::{self, Cursor, Read, Write}; use std::os::unix::io::{AsRawFd, FromRawFd, RawFd}; use std::os::unix::net::UnixDatagram; use std::path::PathBuf; use std::process; use std::rc::Rc; use enumflags2::BitFlags; #[cfg(feature = "libsystemd")] use libsystemd::daemon::{self, NotifyState}; use mio::unix::EventedFd; use mio::*; use nix::sched::{setns, CloneFlags}; use structopt::{clap::ArgGroup, StructOpt}; use zbus::dbus_interface; #[macro_use] extern crate lazy_static; mod tun; #[derive(Debug, StructOpt)] #[structopt( name = "libslirp-helper", about = "slirp helper process", rename_all = "kebab-case", group = ArgGroup::with_name("verb").required(true) )] struct Opt { /// Activate debug mode #[structopt(long)] debug: bool, /// Print capabilities #[structopt(long, group = "verb")] print_capabilities: bool, /// Exit with parent process #[structopt(long)] exit_with_parent: bool, /// DBus bus address #[structopt(long)] dbus_address: Option, /// Helper instance ID #[structopt(long, name = "id")] dbus_id: Option, /// Incoming migration data from DBus #[structopt(long)] dbus_incoming: bool, /// Unix datagram socket path #[structopt(long, parse(from_os_str), group = "verb")] socket_path: Option, /// Unix datagram socket file descriptor #[structopt(long, group = "verb")] fd: Option, /// Incoming migration data #[structopt(long)] incoming_fd: Option, /// Set DHCP NBP URL (ex: tftp://10.0.0.1/my-nbp) #[structopt(long, name = "url")] dhcp_nbp: Option, /// Path to network namespace to join. #[structopt(long)] netns: Option, /// Interface name, such as "tun0". #[structopt(long, group = "verb")] interface: Option, #[structopt(flatten)] slirp: libslirp::Opt, } fn set_exit_with_parent() { #[cfg(any(target_os = "linux", target_os = "android"))] unsafe { libc::prctl(libc::PR_SET_PDEATHSIG, libc::SIGTERM, 0, 0, 0); } } const DBUS_TOKEN: Token = Token(10_000_000); fn slirp_state_read<'a, R: Read>( slirp: &libslirp::MioHandler<'a>, reader: &mut R, ) -> Result<(), Box> { let mut buf = [0; 4]; reader.read(&mut buf)?; let in_version = i32::from_be_bytes(buf); if in_version > libslirp::state_version() { return Err(format!( "Incompatible migration data version: {} > {}", in_version, libslirp::state_version() ) .into()); } slirp.ctxt.state_read(in_version, reader)?; slirp.register(); Ok(()) } fn print_capabilities() -> Result<(), Box> { io::stdout().write_all( r#"{ "type": "slirp-helper", "features": [ "dbus-address", "dhcp", "exit-with-parent", "migrate", "tftp", "ipv4", "ipv6", "netns", "notify-socket", "restrict" ] } "# .as_bytes(), )?; Ok(()) } fn set_netns(fd: RawFd) -> Result<(), nix::Error> { setns(fd, CloneFlags::CLONE_NEWNET) } lazy_static! { // XXX: when do we get async yet? static ref POLL: Poll = Poll::new().unwrap(); } struct Slirp1 { slirp: Rc>, } #[dbus_interface(name = "org.freedesktop.Slirp1.Helper")] impl Slirp1 { fn get_info(&self) -> String { self.slirp.ctxt.connection_info().to_string() } } struct VMState1 { id: String, slirp: Rc>, } #[dbus_interface(name = "org.qemu.VMState1")] impl VMState1 { fn save(&self) -> zbus::fdo::Result> { let mut data = libslirp::state_version().to_be_bytes().to_vec(); let mut state = self .slirp .ctxt .state_get() .map_err(|e| zbus::fdo::Error::Failed(format!("Failed to save: {}", e)))?; data.append(&mut state); Ok(data) } fn load(&self, data: &[u8]) -> zbus::fdo::Result<()> { let mut data = Cursor::new(data); Ok(slirp_state_read(&self.slirp, &mut data) .map_err(|e| zbus::fdo::Error::Failed(format!("Failed to load: {}", e)))?) } #[dbus_interface(property)] fn id(&self) -> &str { &self.id } } fn main() -> Result<(), Box> { let m = Opt::clap().get_matches(); let mut opt = Opt::from_clap(&m); if opt.debug { dbg!(&opt); } if opt.print_capabilities { return print_capabilities(); } if m.occurrences_of("dhcp-start") == 0 { let dhcp_start = opt.slirp.ipv4.net.nth(15).expect("Invalid --net"); opt.slirp.ipv4.dhcp_start = dhcp_start; } if let Some(url) = &opt.dhcp_nbp { let url = url::Url::parse(url)?; if url.scheme() != "tftp" { return Err("Invalid NBP URL".into()); } opt.slirp.tftp.name = Some(url.host_str().unwrap().to_string()); opt.slirp.tftp.bootfile = Some(url.path().to_string()); } let mut main_netns = None; if let Some(netns) = &opt.netns { main_netns = Some(File::open("/proc/self/ns/net")?); let netns = File::open(netns)?; set_netns(netns.as_raw_fd())?; opt.interface.get_or_insert("tun0".to_string()); } let stream = match &opt { Opt { fd: Some(fd), .. } => unsafe { UnixDatagram::from_raw_fd(*fd) }, Opt { socket_path: Some(path), .. } => UnixDatagram::bind(path)?, Opt { interface: Some(tun), .. } => tun::open(tun)?, _ => return Err("Missing a socket argument".into()), }; if let Some(netns) = main_netns { set_netns(netns.as_raw_fd())?; } if opt.exit_with_parent { set_exit_with_parent(); } let slirp = Rc::new(libslirp::MioHandler::new(&opt.slirp, &POLL, stream)); let dbus = if let Some(dbus_addr) = opt.dbus_address { if opt.dbus_id.is_none() { return Err("You must specify an id with DBus".into()); } let c = zbus::Connection::new_for_address(&dbus_addr, true)?; zbus::fdo::DBusProxy::new(&c)?.request_name( &format!("org.freedesktop.Slirp1_{}", process::id()), BitFlags::empty(), )?; zbus::fdo::DBusProxy::new(&c)?.request_name("org.qemu.VMState1", BitFlags::empty())?; let dbus_fd = c.as_raw_fd(); POLL.register( &EventedFd(&dbus_fd), DBUS_TOKEN, Ready::readable(), PollOpt::level(), )?; Some(c) } else { None }; let mut s = if let Some(c) = &dbus { let mut s = zbus::ObjectServer::new(c); s.at( &"/org/freedesktop/Slirp1/Helper".try_into()?, Slirp1 { slirp: slirp.clone(), }, )?; s.at( &"/org/qemu/VMState1".try_into()?, VMState1 { id: opt.dbus_id.unwrap(), slirp: slirp.clone(), }, )?; Some(s) } else { None }; if opt.dbus_incoming && opt.incoming_fd.is_some() { return Err("Invalid multiple incoming paths.".into()); } let mut events = Events::with_capacity(1024); let mut duration = None; if let Some(fd) = opt.incoming_fd { let mut f = unsafe { File::from_raw_fd(fd) }; slirp_state_read(&slirp, &mut f)?; } else if !opt.dbus_incoming { slirp.register(); } #[cfg(feature = "libsystemd")] daemon::notify(true, &[NotifyState::Ready])?; loop { if opt.debug { dbg!(duration); } POLL.poll(&mut events, duration)?; duration = slirp.dispatch(&events)?; if let Some(dbus) = &dbus { for event in &events { match event.token() { DBUS_TOKEN => { let m = dbus.receive_message()?; if let Err(e) = s.as_mut().unwrap().dispatch_message(&m) { eprintln!("{}", e); } } _ => { continue; } } } } } } libslirp-4.3.0/src/bin/libslirp-helper/tun.rs010064400017500001750000000040511374401444000173300ustar 00000000000000use nix::fcntl::OFlag; use nix::ioctl_write_ptr; use nix::sys::stat::Mode; use std::error::Error; use std::os::raw::c_short; use std::os::unix::io::FromRawFd; use std::os::unix::net::UnixDatagram; //pub const IFF_TUN: c_short = 0x0001; pub const IFF_TAP: c_short = 0x0002; pub const IFF_NO_PI: c_short = 0x1000; const INTERFACE_NAME_SIZE: usize = 16; const INTERFACE_REQUEST_UNION_SIZE: usize = 24; const TUN_MAGIC: u8 = b'T'; const TUN_SETIFF: u8 = 202; #[repr(C)] #[derive(Default)] pub struct InterfaceRequest { pub interface_name: [u8; INTERFACE_NAME_SIZE], pub union: InterfaceRequestUnion, } impl InterfaceRequest { pub fn with_interface_name(name: &str) -> Result> { let mut interface_request: Self = Default::default(); interface_request.set_interface_name(name)?; Ok(interface_request) } pub fn set_interface_name(&mut self, name: &str) -> Result<(), Box> { let name_len = name.len(); let mut name = Vec::from(name); if name_len < INTERFACE_NAME_SIZE { name.resize(INTERFACE_NAME_SIZE, 0); } else { return Err("interface name too long".into()); } assert_eq!(name.len(), INTERFACE_NAME_SIZE); self.interface_name.clone_from_slice(&name); Ok(()) } } #[repr(C)] pub union InterfaceRequestUnion { pub data: [u8; INTERFACE_REQUEST_UNION_SIZE], pub flags: c_short, } impl Default for InterfaceRequestUnion { fn default() -> Self { InterfaceRequestUnion { data: Default::default(), } } } ioctl_write_ptr!(tun_set_iff, TUN_MAGIC, TUN_SETIFF, libc::c_int); pub fn open(name: &str) -> Result> { let flags = IFF_TAP | IFF_NO_PI; let fd = nix::fcntl::open("/dev/net/tun", OFlag::O_RDWR, Mode::empty())?; let mut ifr = InterfaceRequest::with_interface_name(name)?; ifr.union.flags = flags; unsafe { tun_set_iff(fd, &mut ifr as *mut InterfaceRequest as *mut i32) }?; Ok(unsafe { UnixDatagram::from_raw_fd(fd) }) } libslirp-4.3.0/src/context.rs010064400017500001750000000423701374401444000143470ustar 00000000000000use libslirp_sys::*; use std::io::{Read, Write}; #[cfg(feature = "structopt")] use crate::Opt; use std::cell::RefCell; use std::ffi::{CStr, CString}; use std::io; use std::net::{Ipv4Addr, Ipv6Addr}; use std::os::raw::{c_char, c_int, c_void}; use std::os::unix::io::RawFd; use std::path::PathBuf; use std::rc::Rc; use std::{fmt, mem, ops, slice, str}; pub struct Context { pub inner: Box>, } pub struct Inner { pub context: *mut Slirp, callbacks: SlirpCb, handler: H, } impl Drop for Context { fn drop(&mut self) { unsafe { slirp_cleanup(self.inner.context); } } } //unsafe impl Send for Inner {} pub trait Handler { type Timer; fn clock_get_ns(&mut self) -> i64; fn send_packet(&mut self, buf: &[u8]) -> io::Result; fn register_poll_fd(&mut self, fd: RawFd); fn unregister_poll_fd(&mut self, fd: RawFd); fn guest_error(&mut self, msg: &str); fn notify(&mut self); fn timer_new(&mut self, func: Box) -> Box; fn timer_mod(&mut self, timer: &mut Box, expire_time: i64); fn timer_free(&mut self, timer: Box); } impl Handler for Rc> { type Timer = T::Timer; fn clock_get_ns(&mut self) -> i64 { self.borrow_mut().clock_get_ns() } fn send_packet(&mut self, buf: &[u8]) -> io::Result { self.borrow_mut().send_packet(buf) } fn register_poll_fd(&mut self, fd: RawFd) { self.borrow_mut().register_poll_fd(fd); } fn unregister_poll_fd(&mut self, fd: RawFd) { self.borrow_mut().unregister_poll_fd(fd); } fn guest_error(&mut self, msg: &str) { self.borrow_mut().guest_error(msg); } fn notify(&mut self) { self.borrow_mut().notify(); } fn timer_new(&mut self, func: Box) -> Box { self.borrow_mut().timer_new(func) } fn timer_mod(&mut self, timer: &mut Box, expire_time: i64) { self.borrow_mut().timer_mod(timer, expire_time) } fn timer_free(&mut self, timer: Box) { self.borrow_mut().timer_free(timer) } } extern "C" fn write_handler_cl(buf: *const c_void, len: usize, opaque: *mut c_void) -> isize { let closure: &mut &mut dyn FnMut(&[u8]) -> isize = unsafe { mem::transmute(opaque) }; let slice = unsafe { slice::from_raw_parts(buf as *const u8, len) }; closure(slice) } extern "C" fn read_handler_cl(buf: *mut c_void, len: usize, opaque: *mut c_void) -> isize { let closure: &mut &mut dyn FnMut(&mut [u8]) -> isize = unsafe { mem::transmute(opaque) }; let slice = unsafe { slice::from_raw_parts_mut(buf as *mut u8, len) }; closure(slice) } #[derive(Copy, PartialEq, Eq, Clone, PartialOrd, Ord)] pub struct PollEvents(usize); impl PollEvents { pub fn empty() -> Self { PollEvents(0) } pub fn poll_in() -> Self { PollEvents(SLIRP_POLL_IN as usize) } pub fn poll_out() -> Self { PollEvents(SLIRP_POLL_OUT as usize) } pub fn poll_pri() -> Self { PollEvents(SLIRP_POLL_PRI as usize) } pub fn poll_err() -> Self { PollEvents(SLIRP_POLL_ERR as usize) } pub fn poll_hup() -> Self { PollEvents(SLIRP_POLL_HUP as usize) } pub fn contains>(&self, other: T) -> bool { let other = other.into(); (*self & other) == other } pub fn is_empty(&self) -> bool { self.0 == 0 } pub fn has_in(&self) -> bool { self.contains(PollEvents::poll_in()) } pub fn has_out(&self) -> bool { self.contains(PollEvents::poll_out()) } pub fn has_pri(&self) -> bool { self.contains(PollEvents::poll_pri()) } pub fn has_err(&self) -> bool { self.contains(PollEvents::poll_err()) } pub fn has_hup(&self) -> bool { self.contains(PollEvents::poll_hup()) } } impl> ops::BitAnd for PollEvents { type Output = PollEvents; fn bitand(self, other: T) -> PollEvents { PollEvents(self.0 & other.into().0) } } impl> ops::BitOr for PollEvents { type Output = PollEvents; fn bitor(self, other: T) -> PollEvents { PollEvents(self.0 | other.into().0) } } impl> ops::BitOrAssign for PollEvents { fn bitor_assign(&mut self, other: T) { self.0 |= other.into().0; } } impl fmt::Debug for PollEvents { fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { let mut one = false; let flags = [ (PollEvents(SLIRP_POLL_IN as usize), "IN"), (PollEvents(SLIRP_POLL_OUT as usize), "OUT"), (PollEvents(SLIRP_POLL_PRI as usize), "PRI"), (PollEvents(SLIRP_POLL_ERR as usize), "ERR"), (PollEvents(SLIRP_POLL_HUP as usize), "HUP"), ]; for &(flag, msg) in &flags { if self.contains(flag) { if one { write!(fmt, " | ")? } write!(fmt, "{}", msg)?; one = true } } if !one { fmt.write_str("(empty)")?; } Ok(()) } } extern "C" fn add_poll_handler_cl(fd: c_int, events: c_int, opaque: *mut c_void) -> c_int { let closure: &mut &mut dyn FnMut(RawFd, PollEvents) -> i32 = unsafe { mem::transmute(opaque) }; closure(fd, PollEvents(events as usize)) } extern "C" fn get_revents_handler_cl(idx: c_int, opaque: *mut c_void) -> c_int { let closure: &mut &mut dyn FnMut(i32) -> PollEvents = unsafe { mem::transmute(opaque) }; closure(idx).0 as c_int } extern "C" fn send_packet_handler( buf: *const c_void, len: usize, opaque: *mut c_void, ) -> isize { let slice = unsafe { slice::from_raw_parts(buf as *const u8, len) }; let res = unsafe { (*(opaque as *mut Inner)).handler.send_packet(slice) }; if res.is_ok() { res.unwrap() as isize } else { eprintln!("send_packet error: {}", res.unwrap_err()); -1 } } extern "C" fn guest_error_handler(msg: *const c_char, opaque: *mut c_void) { let msg = str::from_utf8(unsafe { CStr::from_ptr(msg) }.to_bytes()).unwrap_or(""); unsafe { (*(opaque as *mut Inner)).handler.guest_error(msg) } } extern "C" fn clock_get_ns_handler(opaque: *mut c_void) -> i64 { unsafe { (*(opaque as *mut Inner)).handler.clock_get_ns() } } extern "C" fn timer_new_handler( cb: SlirpTimerCb, cb_opaque: *mut c_void, opaque: *mut c_void, ) -> *mut c_void { let func = Box::new(move || { if let Some(cb) = cb { unsafe { cb(cb_opaque); } } }); let timer = unsafe { (*(opaque as *mut Inner)).handler.timer_new(func) }; Box::into_raw(timer) as *mut c_void } extern "C" fn timer_free_handler(timer: *mut c_void, opaque: *mut c_void) { unsafe { let timer = Box::from_raw(timer as *mut H::Timer); (*(opaque as *mut Inner)).handler.timer_free(timer); } } extern "C" fn timer_mod_handler( timer: *mut c_void, expire_time: i64, opaque: *mut c_void, ) { unsafe { let mut timer = Box::from_raw(timer as *mut H::Timer); (*(opaque as *mut Inner)) .handler .timer_mod(&mut timer, expire_time); Box::into_raw(timer); } } extern "C" fn register_poll_fd_handler(fd: c_int, opaque: *mut c_void) { unsafe { (*(opaque as *mut Inner)).handler.register_poll_fd(fd) } } extern "C" fn unregister_poll_fd_handler(fd: c_int, opaque: *mut c_void) { unsafe { (*(opaque as *mut Inner)).handler.unregister_poll_fd(fd) } } extern "C" fn notify_handler(opaque: *mut c_void) { unsafe { (*(opaque as *mut Inner)).handler.notify() } } impl Context { #[cfg(feature = "structopt")] pub fn new_with_opt(opt: &Opt, handler: H) -> Self { let cstr_vdns: Vec<_> = opt .dns_suffixes .iter() .map(|arg| CString::new(arg.clone().into_bytes()).unwrap()) .collect(); let mut p_vdns: Vec<_> = cstr_vdns.iter().map(|arg| arg.as_ptr()).collect(); p_vdns.push(std::ptr::null()); let as_ptr = |p: &Option| p.as_ref().map_or(std::ptr::null(), |s| s.as_ptr()); let tftp_path = opt .tftp .root .as_ref() .and_then(|s| CString::new(s.to_string_lossy().into_owned()).ok()); let vhostname = opt.hostname.clone().and_then(|s| CString::new(s).ok()); let tftp_server_name = opt.tftp.name.clone().and_then(|s| CString::new(s).ok()); let tftp_bootfile = opt.tftp.bootfile.clone().and_then(|s| CString::new(s).ok()); let vdomainname = opt.domainname.clone().and_then(|s| CString::new(s).ok()); let config = SlirpConfig { version: 2, restricted: opt.restrict as i32, in_enabled: !opt.ipv4.disable, vnetwork: opt.ipv4.net.ip().into(), vnetmask: opt.ipv4.net.mask().into(), vhost: opt.ipv4.host.into(), in6_enabled: !opt.ipv6.disable, vprefix_addr6: opt.ipv6.net6.ip().into(), vprefix_len: opt.ipv6.net6.prefix(), vhost6: opt.ipv6.host.into(), vhostname: as_ptr(&vhostname), tftp_server_name: as_ptr(&tftp_server_name), tftp_path: as_ptr(&tftp_path), bootfile: as_ptr(&tftp_bootfile), vdhcp_start: opt.ipv4.dhcp_start.into(), vnameserver: opt.ipv4.dns.into(), vnameserver6: opt.ipv6.dns.into(), vdnssearch: p_vdns.as_ptr() as *mut *const _, vdomainname: as_ptr(&vdomainname), if_mtu: opt.mtu, if_mru: opt.mtu, disable_host_loopback: opt.disable_host_loopback, enable_emu: false, outbound_addr: std::ptr::null(), outbound_addr6: std::ptr::null(), disable_dns: false, }; Self::new_with_config(&config, handler) } pub fn new_with_config(config: &SlirpConfig, handler: H) -> Self { let mut ret = Context { inner: Box::new(Inner { context: std::ptr::null_mut(), callbacks: SlirpCb { send_packet: Some(send_packet_handler::), guest_error: Some(guest_error_handler::), clock_get_ns: Some(clock_get_ns_handler::), timer_new: Some(timer_new_handler::), timer_free: Some(timer_free_handler::), timer_mod: Some(timer_mod_handler::), register_poll_fd: Some(register_poll_fd_handler::), unregister_poll_fd: Some(unregister_poll_fd_handler::), notify: Some(notify_handler::), }, handler, }), }; let ptr = &*ret.inner as *const _ as *mut _; ret.inner.context = unsafe { slirp_new(config as *const _, &ret.inner.callbacks, ptr) }; assert!(!ret.inner.context.is_null()); ret } pub fn new( restricted: bool, ipv4_enabled: bool, vnetwork: Ipv4Addr, vnetmask: Ipv4Addr, vhost: Ipv4Addr, ipv6_enabled: bool, vprefix_addr6: Ipv6Addr, vprefix_len: u8, vhost6: Ipv6Addr, vhostname: Option, tftp_server_name: Option, tftp_path: Option, tftp_bootfile: Option, vdhcp_start: Ipv4Addr, vnameserver: Ipv4Addr, vnameserver6: Ipv6Addr, vdnssearch: Vec, vdomainname: Option, handler: H, ) -> Self { let mut ret = Context { inner: Box::new(Inner { context: std::ptr::null_mut(), callbacks: SlirpCb { send_packet: Some(send_packet_handler::), guest_error: Some(guest_error_handler::), clock_get_ns: Some(clock_get_ns_handler::), timer_new: Some(timer_new_handler::), timer_free: Some(timer_free_handler::), timer_mod: Some(timer_mod_handler::), register_poll_fd: Some(register_poll_fd_handler::), unregister_poll_fd: Some(unregister_poll_fd_handler::), notify: Some(notify_handler::), }, handler, }), }; let cstr_vdns: Vec<_> = vdnssearch .iter() .map(|arg| CString::new(arg.clone().into_bytes()).unwrap()) .collect(); let mut p_vdns: Vec<_> = cstr_vdns.iter().map(|arg| arg.as_ptr()).collect(); p_vdns.push(std::ptr::null()); let as_ptr = |p: &Option| p.as_ref().map_or(std::ptr::null(), |s| s.as_ptr()); let tftp_path = tftp_path.and_then(|s| CString::new(s.to_string_lossy().into_owned()).ok()); let vhostname = vhostname.and_then(|s| CString::new(s).ok()); let tftp_server_name = tftp_server_name.and_then(|s| CString::new(s).ok()); let tftp_bootfile = tftp_bootfile.and_then(|s| CString::new(s).ok()); let vdomainname = vdomainname.and_then(|s| CString::new(s).ok()); let ptr = &*ret.inner as *const _ as *mut _; ret.inner.context = unsafe { slirp_init( restricted as i32, ipv4_enabled, vnetwork.into(), vnetmask.into(), vhost.into(), ipv6_enabled, vprefix_addr6.into(), vprefix_len, vhost6.into(), as_ptr(&vhostname), as_ptr(&tftp_server_name), as_ptr(&tftp_path), as_ptr(&tftp_bootfile), vdhcp_start.into(), vnameserver.into(), vnameserver6.into(), p_vdns.as_ptr() as *mut *const _, as_ptr(&vdomainname), &ret.inner.callbacks, ptr, ) }; assert!(!ret.inner.context.is_null()); ret } pub fn input(&self, buf: &[u8]) { unsafe { slirp_input(self.inner.context, buf.as_ptr(), buf.len() as i32); } } pub fn connection_info(&self) -> &str { str::from_utf8( unsafe { CStr::from_ptr(slirp_connection_info(self.inner.context)) }.to_bytes(), ) .unwrap_or("") } pub fn pollfds_fill(&self, timeout: &mut u32, mut add_poll_cb: F) where F: FnMut(RawFd, PollEvents) -> i32, { let mut cb: &mut dyn FnMut(RawFd, PollEvents) -> i32 = &mut add_poll_cb; let cb = &mut cb; unsafe { slirp_pollfds_fill( self.inner.context, timeout, Some(add_poll_handler_cl), cb as *mut _ as *mut c_void, ); } } pub fn pollfds_poll(&self, error: bool, mut get_revents_cb: F) where F: FnMut(i32) -> PollEvents, { let mut cb: &mut dyn FnMut(i32) -> PollEvents = &mut get_revents_cb; let cb = &mut cb; unsafe { slirp_pollfds_poll( self.inner.context, error as i32, Some(get_revents_handler_cl), cb as *mut _ as *mut c_void, ); } } pub fn state_save(&self, mut write_cb: F) where F: FnMut(&[u8]) -> isize, { let mut cb: &mut dyn FnMut(&[u8]) -> isize = &mut write_cb; let cb = &mut cb; unsafe { slirp_state_save( self.inner.context, Some(write_handler_cl), cb as *mut _ as *mut c_void, ); } } pub fn state_write(&self, writer: &mut F) -> std::io::Result { let mut res = Ok(0); self.state_save(|buf| match writer.write(buf) { Ok(n) => { res = Ok(*res.as_ref().unwrap() + n); n as isize } Err(e) => { res = Err(e); -1 } }); res } pub fn state_get(&self) -> std::io::Result> { let mut state = vec![]; self.state_write(&mut state)?; Ok(state) } pub fn state_load(&self, version_id: i32, mut read_cb: F) where F: FnMut(&mut [u8]) -> isize, { let mut cb: &mut dyn FnMut(&mut [u8]) -> isize = &mut read_cb; let cb = &mut cb; unsafe { slirp_state_load( self.inner.context, version_id, Some(read_handler_cl), cb as *mut _ as *mut c_void, ); } } pub fn state_read(&self, version_id: i32, reader: &mut R) -> std::io::Result { let mut res = Ok(0); self.state_load(version_id, |buf| match reader.read(buf) { Ok(n) => { res = Ok(*res.as_ref().unwrap() + n); n as isize } Err(e) => { res = Err(e); -1 } }); res } } libslirp-4.3.0/src/lib.rs010064400017500001750000000005651374401444000134310ustar 00000000000000pub mod context; #[cfg(all(feature = "structopt", feature = "mio"))] pub mod mio; #[cfg(feature = "structopt")] pub mod opt; pub mod version; pub use self::context::{Context, Handler, PollEvents}; #[cfg(all(feature = "structopt", feature = "mio"))] pub use self::mio::*; #[cfg(feature = "structopt")] pub use self::opt::*; pub use self::version::{state_version, version}; libslirp-4.3.0/src/mio.rs010064400017500001750000000205711374401444000134460ustar 00000000000000use crate::context::{Context, Handler, PollEvents}; use crate::opt::Opt; use mio::unix::{EventedFd, UnixReady}; use mio::*; use mio_extras::timer::Timer as MioTimer; use slab::Slab; use std::cell::RefCell; use std::fmt; use std::fs::File; use std::io; use std::io::prelude::*; use std::os::unix::io::{AsRawFd, FromRawFd, IntoRawFd, RawFd}; use std::os::unix::net::UnixDatagram; use std::rc::Rc; use std::time::{Duration, Instant}; struct MyTimer { func: Rc>>, timer: MioTimer<()>, } impl fmt::Debug for MyTimer { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { write!(f, "MyTimer {{}}") } } #[derive(Debug)] struct MyFd { fd: RawFd, events: PollEvents, revents: Option, } impl MyFd { fn new(fd: RawFd, events: PollEvents) -> Self { Self { events, fd, revents: None, } } } #[derive(Debug)] enum MyToken { Fd(MyFd), Timer(MyTimer), } pub struct Inner<'a> { start: Instant, stream: UnixDatagram, poll: &'a Poll, tokens: Slab, } pub struct MioHandler<'a> { inner: Rc>>, pub ctxt: Context>>>, } impl<'a> Handler for Inner<'a> { type Timer = usize; fn clock_get_ns(&mut self) -> i64 { const NANOS_PER_SEC: u64 = 1_000_000_000; let d = self.start.elapsed(); (d.as_secs() * NANOS_PER_SEC + d.subsec_nanos() as u64) as i64 } fn timer_new(&mut self, func: Box) -> Box { let timer = MioTimer::default(); let tok = self.tokens.insert(MyToken::Timer(MyTimer { func: Rc::new(RefCell::new(func)), timer, })); let timer = match &self.tokens[tok] { MyToken::Timer(MyTimer { timer: t, .. }) => t, _ => panic!(), }; self.poll .register(timer, Token(tok), Ready::readable(), PollOpt::edge()) .unwrap(); Box::new(tok) } fn timer_mod(&mut self, timer: &mut Box, expire_time: i64) { let when = Duration::from_millis(expire_time as u64); let timer = match &mut self.tokens[**timer] { MyToken::Timer(MyTimer { timer: t, .. }) => t, _ => panic!(), }; timer.set_timeout(when, ()); } fn timer_free(&mut self, timer: Box) { let t = match &self.tokens[*timer] { MyToken::Timer(MyTimer { timer: t, .. }) => t, _ => panic!(), }; self.poll.deregister(t).unwrap(); self.tokens.remove(*timer); drop(timer); // for clarity } fn send_packet(&mut self, buf: &[u8]) -> io::Result { self.stream.send(buf) } fn guest_error(&mut self, msg: &str) { eprintln!("guest error: {}", msg); } fn register_poll_fd(&mut self, _fd: RawFd) {} fn unregister_poll_fd(&mut self, _fd: RawFd) {} fn notify(&mut self) {} } fn to_mio_ready(events: PollEvents) -> mio::Ready { let mut ready = UnixReady::from(Ready::empty()); if events.has_in() { ready.insert(Ready::readable()); } if events.has_out() { ready.insert(Ready::writable()); } if events.has_hup() { ready.insert(UnixReady::hup()); } if events.has_err() { ready.insert(UnixReady::error()); } if events.has_pri() { ready.insert(UnixReady::priority()); } Ready::from(ready) } #[cfg(test)] mod tests { use super::*; #[test] fn to_mio_ready_test() { assert_eq!(to_mio_ready(PollEvents::empty()), Ready::empty()); assert_eq!(to_mio_ready(PollEvents::poll_in()), Ready::readable()); assert_eq!(to_mio_ready(PollEvents::poll_out()), Ready::writable()); assert_eq!( to_mio_ready(PollEvents::poll_err()), Ready::from(UnixReady::error()) ); assert_eq!( to_mio_ready(PollEvents::poll_pri()), Ready::from(UnixReady::priority()) ); assert_eq!( to_mio_ready(PollEvents::poll_hup()), Ready::from(UnixReady::hup()) ); let ev = PollEvents::poll_in() | PollEvents::poll_pri(); let ev = to_mio_ready(ev); assert!(ev.is_readable()); // bug, see https://github.com/carllerche/mio/pull/897 assert!(!ev.is_writable()); } } fn from_mio_ready(ready: mio::Ready) -> PollEvents { let mut events = PollEvents::empty(); let ready = UnixReady::from(ready); if ready.is_readable() { events |= PollEvents::poll_in(); } if ready.is_writable() { events |= PollEvents::poll_out(); } if ready.is_hup() { events |= PollEvents::poll_hup(); } if ready.is_error() { events |= PollEvents::poll_err(); } if ready.is_priority() { events |= PollEvents::poll_pri(); } events } const SOCKET: Token = Token(1_000_000); impl<'a> MioHandler<'a> { pub fn new(opt: &Opt, poll: &'a Poll, stream: UnixDatagram) -> Self { let inner = Rc::new(RefCell::new(Inner { start: Instant::now(), poll, stream, tokens: Slab::with_capacity(1024), })); Self { inner: inner.clone(), ctxt: Context::new_with_opt(opt, inner.clone()), } } pub fn register(&self) { let inner = self.inner.borrow(); let fd = inner.stream.as_raw_fd(); inner .poll .register(&EventedFd(&fd), SOCKET, Ready::readable(), PollOpt::level()) .unwrap(); } pub fn dispatch(&self, events: &Events) -> io::Result> { let inner = self.inner.clone(); for (_, token) in inner.borrow().tokens.iter() { if let MyToken::Fd(fd) = token { let ev = EventedFd(&fd.fd); inner.borrow().poll.deregister(&ev)?; } } for event in events { match event.token() { SOCKET => { const NET_BUFSIZE: usize = 4096 + 65536; // defined by Emu let mut buffer = [0; NET_BUFSIZE]; let fd = self.inner.borrow_mut().stream.as_raw_fd(); let mut f = unsafe { File::from_raw_fd(fd) }; let len = f.read(&mut buffer[..]).unwrap(); f.into_raw_fd(); self.ctxt.input(&buffer[..len]); } i if i.0 < inner.borrow().tokens.capacity() => { let events = from_mio_ready(event.readiness()); let mut inner = inner.borrow_mut(); let token = &mut inner.tokens[i.0]; match token { MyToken::Fd(fd) => { // libslirp doesn't like getting more events... fd.revents = Some(events & fd.events); } MyToken::Timer(MyTimer { func, .. }) => { let func = func.clone(); drop(inner); let func = &mut **func.borrow_mut(); func(); } } } _ => continue, } } self.ctxt.pollfds_poll(false, |idx| { let token = &mut inner.borrow_mut().tokens[idx as usize]; if let MyToken::Fd(fd) = token { fd.revents.take().unwrap_or(PollEvents::empty()) } else { panic!(); } }); inner .borrow_mut() .tokens .retain(|_, v| if let MyToken::Fd(_) = v { false } else { true }); let mut timeout = 0; self.ctxt.pollfds_fill(&mut timeout, |fd, events| { let ready = to_mio_ready(events); let tok = inner .borrow_mut() .tokens .insert(MyToken::Fd(MyFd::new(fd, events))); let ev = EventedFd(&fd); inner .borrow() .poll .register(&ev, Token(tok), ready, PollOpt::level()) .unwrap(); tok as i32 }); let duration = if timeout == 0 { None } else { Some(Duration::from_millis(timeout as u64)) }; Ok(duration) } } libslirp-4.3.0/src/opt.rs010064400017500001750000000055001374401444000134570ustar 00000000000000use ipnetwork::{Ipv4Network, Ipv6Network}; use std::net::{Ipv4Addr, Ipv6Addr}; use std::path::PathBuf; use structopt::StructOpt; #[derive(Debug, StructOpt)] pub struct OptIpv4 { /// Whether to disable IPv4 #[structopt(name = "disable-ipv4", long = "disable-ipv4")] pub disable: bool, /// IPv4 network CIDR #[structopt(name = "net", long = "net", default_value = "10.0.2.0/24")] pub net: Ipv4Network, /// Guest-visible address of the host #[structopt(long, default_value = "10.0.2.2")] pub host: Ipv4Addr, /// The first of the 16 IPs the built-in DHCP server can assign #[structopt( name = "dhcp-start", long = "dhcp-start", short, default_value = "10.0.2.15" )] pub dhcp_start: Ipv4Addr, /// Guest-visible address of the virtual nameserver #[structopt(long = "dhcp-dns", default_value = "10.0.2.3")] pub dns: Ipv4Addr, } #[derive(Debug, StructOpt)] pub struct OptIpv6 { /// Whether to disable IPv6 #[structopt(name = "disable-ipv6", long = "disable-ipv6")] pub disable: bool, /// IPv6 network CIDR #[structopt(name = "net6", long = "net6", default_value = "fec0::/64")] pub net6: Ipv6Network, /// Guest-visible IPv6 address of the host #[structopt(name = "host-ipv6", long = "host-ipv6", default_value = "fec0::2")] pub host: Ipv6Addr, /// Guest-visible address of the virtual nameserver #[structopt(name = "dns-ipv6", long, default_value = "fec0::3")] pub dns: Ipv6Addr, } #[derive(Debug, StructOpt)] pub struct OptTftp { /// RFC2132 "TFTP server name" string #[structopt(name = "name", long = "tftp-name")] pub name: Option, /// root directory of the built-in TFTP server #[structopt(name = "root-path", parse(from_os_str), long = "tftp")] pub root: Option, /// BOOTP filename, for use with tftp #[structopt(long = "dhcp-bootfile")] pub bootfile: Option, } #[derive(Debug, StructOpt)] #[structopt(name = "slirp-opt")] pub struct Opt { /// Isolate guest from host #[structopt(long, short)] pub restrict: bool, /// Set interface MTU #[structopt(long, default_value = "1500")] pub mtu: usize, /// Prohibit connection to 127.0.0.1 #[structopt(long)] pub disable_host_loopback: bool, /// Client hostname reported by the builtin DHCP server #[structopt(long)] pub hostname: Option, /// List of DNS suffixes to search, passed as DHCP option to the guest #[structopt(long = "dns-suffixes")] pub dns_suffixes: Vec, /// Guest-visible domain name of the virtual nameserver from DHCP server #[structopt(long)] pub domainname: Option, #[structopt(flatten)] pub ipv4: OptIpv4, #[structopt(flatten)] pub ipv6: OptIpv6, #[structopt(flatten)] pub tftp: OptTftp, } libslirp-4.3.0/src/version.rs010064400017500001750000000004051374401444000143410ustar 00000000000000use libslirp_sys::*; use std::ffi::CStr; use std::str; pub fn version() -> &'static str { str::from_utf8(unsafe { CStr::from_ptr(slirp_version_string()) }.to_bytes()).unwrap_or("") } pub fn state_version() -> i32 { unsafe { slirp_state_version() } } libslirp-4.3.0/tests/__init__.py010064400017500001750000000000001374401444000147610ustar 00000000000000libslirp-4.3.0/tests/base.py010064400017500001750000000225161374401444000141540ustar 00000000000000import ctypes import functools import io import json import os import pathlib import shlex import signal import socket import subprocess import tempfile import unittest from scapy.all import StreamSocket, sndrcv, Ether, conf, Route, ARP SLIRPHELPER = os.environ.get("SLIRPHELPER") LIBC = ctypes.CDLL("libc.so.6") CLONE_NEWNET = 0x40000000 ORIGINAL_NET_NS = open("/proc/self/ns/net", "rb") THISDIR = pathlib.Path(__file__).parent.absolute() DBUS_SESSION_BUS_ADDRESS = os.environ.get("DBUS_SESSION_BUS_ADDRESS") @functools.lru_cache() def helper_capabilities(): p = subprocess.run( [SLIRPHELPER, "--print-capabilities"], stdout=subprocess.PIPE, text=True ) return json.loads(p.stdout) def has_cap(cap): return cap in helper_capabilities()["features"] class Process: def __init__(self, argv, close_fds=True, env=None): self.p = subprocess.Popen( argv, stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True, close_fds=close_fds, env=env, ) self.rc = None def stdout_all(self): return self.p.stdout.read() def stdout_line(self): return self.p.stdout.readline() def stderr_all(self): return self.p.stderr.read() def stderr_line(self): return self.p.stderr.readline() def close(self, kill=True): """Returns process return code.""" if self.p: if kill: # Ensure the process registers two signals by sending a combo of # SIGINT and SIGTERM. Sending the same signal two times is racy # because the process can't reliably detect how many times the # signal was sent. self.p.send_signal(signal.SIGINT) self.p.send_signal(signal.SIGTERM) self.rc = self.p.wait() self.p.stderr.close() self.p.stdout.close() self.p = None return self.rc def graceful_stop(self, wait=True): self.p.send_signal(signal.SIGINT) if wait: self.p.wait() class TestCase(unittest.TestCase): has_notify_socket = None execno = 0 def setUp(self): if self.has_notify_socket is None: self.has_notify_socket = has_cap("notify-socket") self.cleanups = None prev_net_fd = open("/proc/self/ns/net", "rb") r = LIBC.unshare(CLONE_NEWNET) if r != 0: self.fail('Are you running within "unshare -Ur" ? Need unshare() syscall.') self.guest_net_fd = open("/proc/self/ns/net", "rb") self._add_teardown(self.guest_net_fd) # mode tap, means ethernet headers os.system( "ip link set lo up;" "ip tuntap add mode tap name tun0;" "ip link set tun0 mtu 65521;" "ip link set tun0 up;" "ip addr add 10.0.2.100/24 dev tun0;" "ip addr add 2001:2::100/32 dev tun0 nodad;" "ip route add 0.0.0.0/0 via 10.0.2.2 dev tun0;" "ip route add ::/0 via 2001:2::2 dev tun0;" ) w = subprocess.Popen(["/bin/sleep", "1073741824"]) self.guest_ns_pid = w.pid self._add_teardown(w) LIBC.setns(prev_net_fd.fileno(), CLONE_NEWNET) prev_net_fd.close() self._tmpdir = tempfile.TemporaryDirectory() self._add_teardown(self._tmpdir) def tearDown(self): while self.cleanups: item = self.cleanups.pop() if isinstance(item, subprocess.Popen): item.send_signal(signal.SIGINT) item.wait() elif isinstance(item, Process): item.close() if getattr(item, "stdout", None): item.stdout.close() if getattr(item, "stderr", None): item.stderr.close() elif isinstance(item, io.BufferedReader): item.close() elif isinstance(item, tempfile.TemporaryDirectory): item.cleanup() else: print("Unknown cleanup type") print(type(item)) def run_helper(self, argv1=[], wait_ready=True, netns=True): if isinstance(argv1, str): argv1 = shlex.split(argv1) a = [SLIRPHELPER] + argv1 if netns: a = a + ["--netns", self.net_ns_path(), "--interface", "tun0"] sn = None env = None if self.has_notify_socket and wait_ready: self.execno += 1 sn = socket.socket(socket.AF_UNIX, socket.SOCK_DGRAM) path = self.get_tmp_filename("sn-%d" % self.execno) sn.bind(path) env = dict(os.environ, NOTIFY_SOCKET="%s" % path) p = Process(a, close_fds=False, env=env) if sn: sn.settimeout(1) # FIXME: remove timeout, end if process exit try: self.assertIn("READY=1", sn.recv(4096).decode()) except: print(p.stderr_all()) sn.close() self._add_teardown(p) return p def skipIfNotCapable(self, cap): if not has_cap(cap): self.skipTest("since '%s' capability is missing" % cap) def start_echo(self, udp=False): cmd = [THISDIR / "echo.py"] if udp: cmd += ["-u"] p = Process(cmd) self._add_teardown(p) return int(p.stdout_line()) def assertTcpEcho(self, ip, port): data = os.getrandom(16) with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s: s.connect((ip, port)) s.sendall(data) self.assertEqual(s.recv(len(data)), data) def assertUdpEcho(self, ip, port): data = os.getrandom(16) with socket.socket(socket.AF_INET, socket.SOCK_DGRAM) as s: s.sendto(data, (ip, port)) self.assertEqual(s.recv(len(data)), data) def get_tmp_filename(self, name): return os.path.join(self._tmpdir.name, name) def _add_teardown(self, item): if not self.cleanups: self.cleanups = [] self.cleanups.append(item) def net_ns_path(self): return "/proc/%s/ns/net" % self.guest_ns_pid def guest_netns(self): xself = self class controlled_execution: def __enter__(self): self.prev_net_fd = open("/proc/self/ns/net", "rb") LIBC.setns(xself.guest_net_fd.fileno(), CLONE_NEWNET) def __exit__(self, type, value, traceback): LIBC.setns(self.prev_net_fd.fileno(), CLONE_NEWNET) self.prev_net_fd.close() return controlled_execution() class testScapySocket: def __init__(self, fd): ss = StreamSocket(fd) ss.basecls = Ether self.ss = ss conf.route = Route() # reinitializes the route based on the NS self.e = Ether(src="52:55:0a:00:02:42") def send(self, x): self.ss.send(self.e / x) def recv(self, x): # this is not symmetrical with send, which appends Ether # header, but ss.basecls will strip it of: not sure if that's # the best way of doing things in fact, but that seem to work.. return self.ss.recv(x) def fileno(self): return self.ss.fileno() def sr1(self, x, checkIPaddr=True, *args, **kwargs): conf.checkIPaddr = checkIPaddr kwargs.setdefault("verbose", False) ans, _ = sndrcv(self.ss, self.e / x, *args, **kwargs) return ans[0][1] def sr(self, x, checkIPaddr=True, *args, **kwargs): conf.checkIPaddr = checkIPaddr kwargs.setdefault("verbose", False) return sndrcv(self.ss, self.e / x, *args, **kwargs) def withScapy(): def decorate(fn): @functools.wraps(fn) def maybe(*args, **kw): sp = socket.socketpair(type=socket.SOCK_DGRAM) os.set_inheritable(sp[0].fileno(), True) self = args[0] arg = kw.pop("parg", "") p = self.run_helper(arg + " --fd %d" % sp[0].fileno(), netns=False) s = testScapySocket(sp[1]) # gratious advertizing ARP s.send(ARP(psrc="10.0.2.100", pdst="10.0.2.100", hwsrc=s.e.src)) kw["s"] = s ret = fn(*args, **kw) sp[0].close() sp[1].close() return ret return maybe return decorate def isolateHostNetwork(): def decorate(fn): @functools.wraps(fn) def maybe(*args, **kw): prev_net_fd = open("/proc/self/ns/net", "rb") r = LIBC.unshare(CLONE_NEWNET) if r != 0: self.fail( 'Are you running within "unshare -Ur" ? Need unshare() syscall.' ) # mode tun, since we don't actually plan on anyone reading the other side. os.system( "ip link set lo up;" "ip tuntap add mode tun name eth0;" "ip link set eth0 mtu 65521;" "ip link set eth0 up;" "ip addr add 192.168.1.100/24 dev eth0;" "ip addr add 3ffe::100/16 dev eth0 nodad;" "ip route add 0.0.0.0/0 via 192.168.1.1 dev eth0;" "ip route add ::/0 via 3ffe::1 dev eth0;" ) ret = fn(*args, **kw) LIBC.setns(prev_net_fd.fileno(), CLONE_NEWNET) prev_net_fd.close() return ret return maybe return decorate libslirp-4.3.0/tests/dbus.conf010064400017500001750000000012511374401444000144650ustar 00000000000000 slirpnetstack-test unix:dir=/tmp EXTERNAL contexts/dbus_contexts libslirp-4.3.0/tests/echo.py010075500017500001750000000012521374401444000141550ustar 00000000000000#!/usr/bin/env python3 import socket import sys import getopt def main(argv): stype = socket.SOCK_STREAM opts, args = getopt.getopt(argv, "u") for opt, arg in opts: if opt == "-u": stype = socket.SOCK_DGRAM s = socket.socket(socket.AF_INET, stype) s.bind(("", 0)) print(s.getsockname()[1], flush=True) if stype == socket.SOCK_STREAM: s.listen(1) s, _ = s.accept() while 1: data, addr = s.recvfrom(1024) if not data: break if addr: s.sendto(data, addr) else: s.sendall(data) s.close() if __name__ == "__main__": main(sys.argv[1:]) libslirp-4.3.0/tests/test-ip.rs010064400017500001750000000063471374401444000146270ustar 00000000000000use etherparse::{PacketBuilder, TcpOptionElement}; use libslirp; use std::io; use std::os::unix::io::RawFd; use std::time::Instant; use structopt::StructOpt; impl libslirp::Handler for App { type Timer = usize; fn clock_get_ns(&mut self) -> i64 { const NANOS_PER_SEC: u64 = 1_000_000_000; let d = self.start.elapsed(); (d.as_secs() * NANOS_PER_SEC + d.subsec_nanos() as u64) as i64 } fn timer_new(&mut self, _func: Box) -> Box { Box::new(0) } fn timer_mod(&mut self, _timer: &mut Box, _expire_time: i64) {} fn timer_free(&mut self, timer: Box) { drop(timer); } fn send_packet(&mut self, buf: &[u8]) -> io::Result { //self.stream.send(buf).unwrap() as isize Ok(buf.len()) } fn guest_error(&mut self, msg: &str) { eprintln!("guest error: {}", msg); } fn register_poll_fd(&mut self, fd: RawFd) { println!("register_poll_fd: fd={:?}", fd); } fn unregister_poll_fd(&mut self, fd: RawFd) { println!("unregister_poll_fd: fd={:?}", fd); } fn notify(&mut self) { println!("notify"); } } struct App { start: Instant, } #[test] fn ip() { let opt = libslirp::Opt::from_args(); let app = App { start: Instant::now(), }; let ctxt = libslirp::Context::new_with_opt(&opt, app); { let builder = PacketBuilder::ethernet2( [1, 2, 3, 4, 5, 6], //source mac [7, 8, 9, 10, 11, 12], //destination mac ) .ipv4( [192, 168, 1, 1], //source ip [192, 168, 1, 2], //desitination ip 20, //time to life ) .udp( 21, //source port 1234, //desitnation port ); //payload of the udp packet let payload = [1, 2, 3, 4, 5, 6, 7, 8]; let mut buffer = Vec::::with_capacity(builder.size(payload.len())); builder.write(&mut buffer, &payload).unwrap(); ctxt.input(&buffer); } { let builder = PacketBuilder::ethernet2( [1, 2, 3, 4, 5, 6], //source mac [7, 8, 9, 10, 11, 12], //destionation mac ) .ipv4( [192, 168, 1, 1], //source ip [192, 168, 1, 2], //desitionation ip 20, //time to life ) .tcp( 21, //source port 1234, //desitnation port 1, //sequence number 26180, //window size ) //set additional tcp header fields .ns() //set the ns flag //supported flags: ns(), fin(), syn(), rst(), psh(), ece(), cwr() .ack(123) //ack flag + the ack number .urg(23) //urg flag + urgent pointer //tcp header options .options(&[ TcpOptionElement::Nop, TcpOptionElement::MaximumSegmentSize(1234), ]) .unwrap(); //payload of the tcp packet let payload = [1, 2, 3, 4, 5, 6, 7, 8]; //get some memory to store the result let mut buffer = Vec::::with_capacity(builder.size(payload.len())); builder.write(&mut buffer, &payload).unwrap(); ctxt.input(&buffer); } } libslirp-4.3.0/tests/test.py010064400017500001750000000202571374401444000142210ustar 00000000000000import json import unittest from . import base from scapy.all import * from ipaddress import IPv4Address from pydbus import SessionBus class CLITest(base.TestCase): def test_help(self): """ Test if -h prints stuff looking like help screen. """ p = self.run_helper("-h", netns=False, wait_ready=False) e = p.stderr_all() self.assertFalse(e) o = p.stdout_all().lower() self.assertIn("usage:", o) def test_print_capabilities(self): """ Test if --print-capabilities output valid json. """ p = self.run_helper("--print-capabilities", netns=False, wait_ready=False) e = p.stderr_all() self.assertFalse(e) o = p.stdout_all() j = json.loads(o) self.assertEqual(j["type"], "slirp-helper") if "features" in j: self.assertIsInstance(j["features"], list) f = set(j["features"]) unknown = f.difference( { "dbus-address", "dhcp", "exit-with-parent", "ipv4", "ipv6", "migrate", "netns", "notify-socket", "restrict", "tftp", } ) for cap in unknown: if not cap.startswith("x-"): self.fail("Unknown capability: %s" % cap) def test_restrict(self): """ Basic test if 'restrict' options exists. """ self.skipIfNotCapable("restrict") self.run_helper("--restrict") def test_ipv4(self): """ Basic test if 'ipv4' options exists. """ self.skipIfNotCapable("ipv4") self.run_helper("--disable-ipv4 --net 12.12.0.1/8") def test_ipv4(self): """ Basic test if 'ipv6' options exists. """ self.skipIfNotCapable("ipv6") self.run_helper("--disable-ipv6 --net6 fec0::/64") def test_exit_with_parent(self): """ Basic test if 'exit-with-parent' option exists. """ self.skipIfNotCapable("exit-with-parent") self.run_helper("--exit-with-parent") def test_tftp(self): """ Basic test if 'tftp' options exists. """ self.skipIfNotCapable("tftp") self.run_helper("--tftp .") def test_net(self): """ Basic test if --net parses successfully. """ p = self.run_helper("--net 12.12.0.1/23") p.graceful_stop() p = self.run_helper("--net wefo/23", wait_ready=False) e = p.stderr_all() self.assertTrue(e) p.graceful_stop() def test_dbus(self): """ Test if --dbus-address works. """ self.skipIfNotCapable("dbus-address") if not base.DBUS_SESSION_BUS_ADDRESS: self.skipTest("DBUS_SESSION_BUS_ADDRESS unset") p = self.run_helper( "--dbus-id TestId --dbus-address %s" % base.DBUS_SESSION_BUS_ADDRESS ) bus = SessionBus() iface = bus.get(".Slirp1_%u" % p.p.pid, "/org/freedesktop/Slirp1/Helper") info = iface.GetInfo() self.assertIn("Protocol[State]", info) class ConnTest(base.TestCase): @base.withScapy() def test_ping(self, s): """ Test Scapy ping """ pkt = s.sr1(IP(dst="10.0.2.2") / ICMP()) self.assertEqual(pkt.sprintf("%ICMP.type%"), "echo-reply") @base.isolateHostNetwork() def test_restrict(self): """ Test --restrict behaviour """ port = self.start_echo() self.run_helper("--restrict") with self.guest_netns(): with self.assertRaises((ConnectionError, ConnectionRefusedError)): self.assertTcpEcho("192.168.1.100", port) @base.isolateHostNetwork() def test_tcp_echo(self): """ Test TCP echo """ port = self.start_echo() self.run_helper() with self.guest_netns(): self.assertTcpEcho("192.168.1.100", port) @base.isolateHostNetwork() def test_udp_echo(self): """ Test UDP echo """ port = self.start_echo(udp=True) self.run_helper() with self.guest_netns(): self.assertUdpEcho("192.168.1.100", port) @unittest.skipUnless(base.has_cap("dhcp"), "Missing 'dhcp' feature") class DHCPTest(base.TestCase): @base.withScapy() def test_dhcp_v4(self, s): """ Test DHCPv4 discover """ bootp = BOOTP(xid=RandInt()) dhcp = DHCP(options=[("message-type", "discover"), "end"]) p = ( IP(src="0.0.0.0", dst="255.255.255.255") / UDP(sport=68, dport=67) / bootp / dhcp ) pkt = s.sr1(p, checkIPaddr=False) self.assertEqual(pkt.sprintf("%BOOTP.op%"), "BOOTREPLY") addr = IPv4Address(pkt[BOOTP].yiaddr) self.assertGreaterEqual(addr, IPv4Address("10.0.2.15")) self.assertLess(addr, IPv4Address("10.0.2.100")) for o in pkt[DHCP].options: if o[0] in ("router", "server_id"): self.assertEqual(o[1], "10.0.2.2") opts = [o[0] for o in pkt[DHCP].options if isinstance(o, tuple)] self.assertIn("router", opts) self.assertIn("name_server", opts) self.assertIn("lease_time", opts) self.assertIn("server_id", opts) @base.withScapy() def dhcp_and_net(self, s): bootp = BOOTP(xid=RandInt()) dhcp = DHCP(options=[("message-type", "discover"), "end"]) p = ( IP(src="0.0.0.0", dst="255.255.255.255") / UDP(sport=68, dport=67) / bootp / dhcp ) pkt = s.sr1(p, checkIPaddr=False) self.assertEqual(pkt.sprintf("%BOOTP.op%"), "BOOTREPLY") addr = IPv4Address(pkt[BOOTP].yiaddr) self.assertGreaterEqual(addr, IPv4Address("12.34.56.15")) self.assertLess(addr, IPv4Address("12.34.56.100")) def test_dhcp_and_net(self): """ Test DHCPv4 and -net """ self.dhcp_and_net(parg="--net 12.34.56.1/24") @base.withScapy() def dhcp_dns(self, s): bootp = BOOTP(xid=RandInt()) dhcp = DHCP(options=[("message-type", "discover"), "end"]) p = ( IP(src="0.0.0.0", dst="255.255.255.255") / UDP(sport=68, dport=67) / bootp / dhcp ) pkt = s.sr1(p, checkIPaddr=False) # BOOTREPLY for o in pkt[DHCP].options: if o[0] == "name_server": self.assertEqual(o[1], "8.8.8.8") return self.fail() def test_dhcp_dns(self): """ Test DHCPv4 DNS option """ self.dhcp_dns(parg="--dhcp-dns 8.8.8.8") @base.withScapy() def dhcp_nbp(self, s): bootp = BOOTP(xid=RandInt()) dhcp = DHCP(options=[("message-type", "discover"), "end"]) p = ( IP(src="0.0.0.0", dst="255.255.255.255") / UDP(sport=68, dport=67) / bootp / dhcp ) pkt = s.sr1(p, checkIPaddr=False) # BOOTREPLY bootFileName = pkt[BOOTP].file.partition(b"\0")[0].decode() tftpServerName = None for o in pkt[DHCP].options: if o[0] == "boot-file-name": bootFileName = o[1].decode() # Higher precedence? elif o[0] in ( 66, "tftp-server-name", ): # FIXME: scapy doesn't know that field? tftpServerName = o[1].decode() self.assertEqual(tftpServerName, "10.0.0.1") self.assertEqual(bootFileName, "/my-nbp") def test_dhcp_nbp(self): """ Test DHCPv4 NBP option """ self.dhcp_nbp(parg="--dhcp-nbp tftp://10.0.0.1/my-nbp") @base.withScapy() def dhcp_bootfile(self, s): bootp = BOOTP(xid=RandInt()) dhcp = DHCP(options=[("message-type", "discover"), "end"]) p = ( IP(src="0.0.0.0", dst="255.255.255.255") / UDP(sport=68, dport=67) / bootp / dhcp ) pkt = s.sr1(p, checkIPaddr=False) # BOOTREPLY self.assertEqual( pkt[BOOTP].file.partition(b"\0")[0].decode(), "http://boot.netboot.xyz/" ) def test_dhcp_bootfile(self): """ Test DHCPv4 bootfile option """ self.dhcp_bootfile(parg="--dhcp-bootfile http://boot.netboot.xyz/")