(linux) wireguard-control: migrate from `wireguard-control-sys` to `netlink` crates (#177)
also introduces a new `netlink-request` crate to help modularize the netlink code. this currently depends on a fork of the `netlink` project, but we should be able to use the official version soon.pull/186/head
parent
7ade68379f
commit
09e68c2c01
|
@ -24,18 +24,18 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "ansi_term"
|
name = "ansi_term"
|
||||||
version = "0.11.0"
|
version = "0.12.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "ee49baf6cb617b853aa8d93bf420db2383fab46d314482ca2803b40d5fde979b"
|
checksum = "d52a9bb7ec0cf484c551830a7ce27bd20d67eac647e1befb56b0be4ee39a55d2"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"winapi",
|
"winapi",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "anyhow"
|
name = "anyhow"
|
||||||
version = "1.0.47"
|
version = "1.0.51"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "38d9ff5d688f1c13395289f67db01d4826b46dd694e7580accdc3e8430f2d98e"
|
checksum = "8b26702f315f53b6071259e15dd9d64528213b44d61de1ec926eca7715d62203"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "atty"
|
name = "atty"
|
||||||
|
@ -60,43 +60,12 @@ version = "0.13.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "904dfeac50f3cdaba28fc6f57fdcddb75f49ed61346676a78c4ffe55877802fd"
|
checksum = "904dfeac50f3cdaba28fc6f57fdcddb75f49ed61346676a78c4ffe55877802fd"
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "bindgen"
|
|
||||||
version = "0.59.1"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "453c49e5950bb0eb63bb3df640e31618846c89d5b7faa54040d76e98e0134375"
|
|
||||||
dependencies = [
|
|
||||||
"bitflags",
|
|
||||||
"cexpr",
|
|
||||||
"clang-sys",
|
|
||||||
"lazy_static",
|
|
||||||
"lazycell",
|
|
||||||
"peeking_take_while",
|
|
||||||
"proc-macro2",
|
|
||||||
"quote",
|
|
||||||
"regex",
|
|
||||||
"rustc-hash",
|
|
||||||
"shlex",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "bitflags"
|
name = "bitflags"
|
||||||
version = "1.3.2"
|
version = "1.3.2"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
|
checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "bitvec"
|
|
||||||
version = "0.19.6"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "55f93d0ef3363c364d5976646a38f04cf67cfe1d4c8d160cdea02cab2c116b33"
|
|
||||||
dependencies = [
|
|
||||||
"funty",
|
|
||||||
"radium",
|
|
||||||
"tap",
|
|
||||||
"wyz",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "byteorder"
|
name = "byteorder"
|
||||||
version = "1.4.3"
|
version = "1.4.3"
|
||||||
|
@ -115,15 +84,6 @@ version = "1.0.72"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "22a9137b95ea06864e018375b72adfb7db6e6f68cfc8df5a04d00288050485ee"
|
checksum = "22a9137b95ea06864e018375b72adfb7db6e6f68cfc8df5a04d00288050485ee"
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "cexpr"
|
|
||||||
version = "0.5.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "db507a7679252d2276ed0dd8113c6875ec56d3089f9225b2b42c30cc1f8e5c89"
|
|
||||||
dependencies = [
|
|
||||||
"nom",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "cfg-if"
|
name = "cfg-if"
|
||||||
version = "1.0.0"
|
version = "1.0.0"
|
||||||
|
@ -136,21 +96,11 @@ version = "1.4.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "fff857943da45f546682664a79488be82e69e43c1a7a2307679ab9afb3a66d2e"
|
checksum = "fff857943da45f546682664a79488be82e69e43c1a7a2307679ab9afb3a66d2e"
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "clang-sys"
|
|
||||||
version = "1.3.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "fa66045b9cb23c2e9c1520732030608b02ee07e5cfaa5a521ec15ded7fa24c90"
|
|
||||||
dependencies = [
|
|
||||||
"glob",
|
|
||||||
"libc",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "clap"
|
name = "clap"
|
||||||
version = "2.33.3"
|
version = "2.34.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "37e58ac78573c40708d45522f0d80fa2f01cc4f9b4e2bf749807255454312002"
|
checksum = "a0610544180c38b88101fecf2dd634b174a62eef6946f84dfc6a7127512b381c"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"ansi_term",
|
"ansi_term",
|
||||||
"atty",
|
"atty",
|
||||||
|
@ -288,40 +238,33 @@ dependencies = [
|
||||||
"percent-encoding",
|
"percent-encoding",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "funty"
|
|
||||||
version = "1.1.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "fed34cd105917e91daa4da6b3728c47b068749d6a62c59811f06ed2ac71d9da7"
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "futures-channel"
|
name = "futures-channel"
|
||||||
version = "0.3.17"
|
version = "0.3.19"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "5da6ba8c3bb3c165d3c7319fc1cc8304facf1fb8db99c5de877183c08a273888"
|
checksum = "ba3dda0b6588335f360afc675d0564c17a77a2bda81ca178a4b6081bd86c7f0b"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"futures-core",
|
"futures-core",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "futures-core"
|
name = "futures-core"
|
||||||
version = "0.3.17"
|
version = "0.3.19"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "88d1c26957f23603395cd326b0ffe64124b818f4449552f960d815cfba83a53d"
|
checksum = "d0c8ff0461b82559810cdccfde3215c3f373807f5e5232b71479bff7bb2583d7"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "futures-task"
|
name = "futures-task"
|
||||||
version = "0.3.17"
|
version = "0.3.19"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "1d3d00f4eddb73e498a54394f228cd55853bdf059259e8e7bc6e69d408892e99"
|
checksum = "6ee7c6485c30167ce4dfb83ac568a849fe53274c831081476ee13e0dce1aad72"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "futures-util"
|
name = "futures-util"
|
||||||
version = "0.3.17"
|
version = "0.3.19"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "36568465210a3a6ee45e1f165136d68671471a501e632e9a98d96872222b5481"
|
checksum = "d9b5cf40b47a271f77a8b1bec03ca09044d99d2372c0de244e66430761127164"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"autocfg",
|
|
||||||
"futures-core",
|
"futures-core",
|
||||||
"futures-task",
|
"futures-task",
|
||||||
"pin-project-lite",
|
"pin-project-lite",
|
||||||
|
@ -349,12 +292,6 @@ dependencies = [
|
||||||
"wasi",
|
"wasi",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "glob"
|
|
||||||
version = "0.3.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "9b919933a397b79c37e33b77bb2aa3dc8eb6e165ad809e58ff75bc7db2e34574"
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "hashbrown"
|
name = "hashbrown"
|
||||||
version = "0.11.2"
|
version = "0.11.2"
|
||||||
|
@ -409,7 +346,7 @@ checksum = "1323096b05d41827dadeaee54c9981958c0f94e670bc94ed80037d1a7b8b186b"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bytes",
|
"bytes",
|
||||||
"fnv",
|
"fnv",
|
||||||
"itoa",
|
"itoa 0.4.8",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
@ -446,9 +383,9 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "hyper"
|
name = "hyper"
|
||||||
version = "0.14.15"
|
version = "0.14.16"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "436ec0091e4f20e655156a30a0df3770fe2900aa301e548e08446ec794b6953c"
|
checksum = "b7ec3e62bdc98a2f0393a5048e4c30ef659440ea6e0e572965103e72bd836f55"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bytes",
|
"bytes",
|
||||||
"futures-channel",
|
"futures-channel",
|
||||||
|
@ -458,7 +395,7 @@ dependencies = [
|
||||||
"http-body",
|
"http-body",
|
||||||
"httparse",
|
"httparse",
|
||||||
"httpdate",
|
"httpdate",
|
||||||
"itoa",
|
"itoa 0.4.8",
|
||||||
"pin-project-lite",
|
"pin-project-lite",
|
||||||
"socket2",
|
"socket2",
|
||||||
"tokio",
|
"tokio",
|
||||||
|
@ -510,29 +447,29 @@ version = "0.4.8"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "b71991ff56294aa922b450139ee08b3bfc70982c6b2c7562771375cf73542dd4"
|
checksum = "b71991ff56294aa922b450139ee08b3bfc70982c6b2c7562771375cf73542dd4"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "itoa"
|
||||||
|
version = "1.0.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "1aab8fc367588b89dcee83ab0fd66b72b50b72fa1904d7095045ace2b0c81c35"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "lazy_static"
|
name = "lazy_static"
|
||||||
version = "1.4.0"
|
version = "1.4.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
|
checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "lazycell"
|
|
||||||
version = "1.3.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "830d08ce1d1d941e6b30645f1a0eb5643013d835ce3779a5fc208261dbe10f55"
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "libc"
|
name = "libc"
|
||||||
version = "0.2.108"
|
version = "0.2.112"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "8521a1b57e76b1ec69af7599e75e38e7b7fad6610f037db8c79b127201b5d119"
|
checksum = "1b03d17f364a3a042d5e5d46b053bbbf82c92c9430c592dd4c064dc6ee997125"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "libsqlite3-sys"
|
name = "libsqlite3-sys"
|
||||||
version = "0.23.1"
|
version = "0.23.2"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "abd5850c449b40bacb498b2bbdfaff648b1b055630073ba8db499caf2d0ea9f2"
|
checksum = "d2cafc7c74096c336d9d27145f7ebd4f4b6f95ba16aa5a282387267e6925cb58"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"pkg-config",
|
"pkg-config",
|
||||||
"vcpkg",
|
"vcpkg",
|
||||||
|
@ -570,9 +507,9 @@ checksum = "308cc39be01b73d0d18f82a0e7b2a3df85245f84af96fdddc5d202d27e47b86a"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "memoffset"
|
name = "memoffset"
|
||||||
version = "0.6.4"
|
version = "0.6.5"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "59accc507f1338036a0477ef61afdae33cde60840f4dfe481319ce3ad116ddf9"
|
checksum = "5aa361d4faea93603064a027415f07bd8e1d5c88c9fbf68bf56a285428fd79ce"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"autocfg",
|
"autocfg",
|
||||||
]
|
]
|
||||||
|
@ -601,9 +538,9 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "netlink-packet-core"
|
name = "netlink-packet-core"
|
||||||
version = "0.2.4"
|
version = "0.4.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "ac48279d5062bdf175bdbcb6b58ff1d6b0ecd54b951f7a0ff4bc0550fe903ccb"
|
checksum = "8349128e95f5dabcb8a18587ad06b3ca7993e90c0c360b4a2abac0313ebce727"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"byteorder",
|
"byteorder",
|
||||||
|
@ -612,10 +549,23 @@ dependencies = [
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "netlink-packet-route"
|
name = "netlink-packet-generic"
|
||||||
version = "0.8.0"
|
version = "0.3.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "76aed5d3b6e3929713bf1e1334a11fd65180b6d9f5d7c8572664c48b122604f8"
|
checksum = "8678ffbbfef3dd88acbe85ed31d32f0de0a100854ee7d47fe5b250f81857a23b"
|
||||||
|
dependencies = [
|
||||||
|
"anyhow",
|
||||||
|
"byteorder",
|
||||||
|
"libc",
|
||||||
|
"netlink-packet-core",
|
||||||
|
"netlink-packet-utils",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "netlink-packet-route"
|
||||||
|
version = "0.10.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "9fb5d54077de7c0904111e1d19b661b8cfccbc23d9ce5b6dbcc7362721e6e552"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"bitflags",
|
"bitflags",
|
||||||
|
@ -627,9 +577,9 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "netlink-packet-utils"
|
name = "netlink-packet-utils"
|
||||||
version = "0.4.1"
|
version = "0.5.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "5fcfb6f758b66e964b2339596d94078218d96aad5b32003e8e2a1d23c27a6784"
|
checksum = "0a008a56eceb0cab06739c7f37f15bda27f1147a14d0e7136e8c913b94f1441d"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"byteorder",
|
"byteorder",
|
||||||
|
@ -638,11 +588,36 @@ dependencies = [
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "netlink-sys"
|
name = "netlink-packet-wireguard"
|
||||||
version = "0.7.0"
|
version = "0.1.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "git+https://github.com/mcginty/netlink?branch=wireguard-fixes#2b60e310ede5fa4c80c00874c19ee755b1bc8249"
|
||||||
checksum = "f48ea34ea0678719815c3753155067212f853ad2d8ef4a49167bae7f7c254188"
|
|
||||||
dependencies = [
|
dependencies = [
|
||||||
|
"anyhow",
|
||||||
|
"byteorder",
|
||||||
|
"libc",
|
||||||
|
"log",
|
||||||
|
"netlink-packet-generic",
|
||||||
|
"netlink-packet-utils",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "netlink-request"
|
||||||
|
version = "0.1.0"
|
||||||
|
dependencies = [
|
||||||
|
"netlink-packet-core",
|
||||||
|
"netlink-packet-generic",
|
||||||
|
"netlink-packet-route",
|
||||||
|
"netlink-packet-wireguard",
|
||||||
|
"netlink-sys",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "netlink-sys"
|
||||||
|
version = "0.8.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "ed51a4602bb956eefef0ebc15f478bf9732fa3cc706e0a37112e654f41c5b92c"
|
||||||
|
dependencies = [
|
||||||
|
"bytes",
|
||||||
"libc",
|
"libc",
|
||||||
"log",
|
"log",
|
||||||
]
|
]
|
||||||
|
@ -660,18 +635,6 @@ dependencies = [
|
||||||
"memoffset",
|
"memoffset",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "nom"
|
|
||||||
version = "6.1.2"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "e7413f999671bd4745a7b624bd370a569fb6bc574b23c83a3c5ed2e453f3d5e2"
|
|
||||||
dependencies = [
|
|
||||||
"bitvec",
|
|
||||||
"funty",
|
|
||||||
"memchr",
|
|
||||||
"version_check",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "ntapi"
|
name = "ntapi"
|
||||||
version = "0.3.6"
|
version = "0.3.6"
|
||||||
|
@ -728,12 +691,6 @@ version = "1.0.6"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "0744126afe1a6dd7f394cb50a716dbe086cb06e255e53d8d0185d82828358fb5"
|
checksum = "0744126afe1a6dd7f394cb50a716dbe086cb06e255e53d8d0185d82828358fb5"
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "peeking_take_while"
|
|
||||||
version = "0.1.2"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "19b17cddbe7ec3f8bc800887bab5e717348c95ea2ca0b1bf0837fb964dc67099"
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "percent-encoding"
|
name = "percent-encoding"
|
||||||
version = "2.1.0"
|
version = "2.1.0"
|
||||||
|
@ -754,9 +711,9 @@ checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "pkg-config"
|
name = "pkg-config"
|
||||||
version = "0.3.22"
|
version = "0.3.24"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "12295df4f294471248581bc09bef3c38a5e46f1e36d6a37353621a0c6c357e1f"
|
checksum = "58893f751c9b0412871a09abd62ecd2a00298c6c83befa223ef98c52aef40cbe"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "ppv-lite86"
|
name = "ppv-lite86"
|
||||||
|
@ -800,9 +757,9 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "proc-macro2"
|
name = "proc-macro2"
|
||||||
version = "1.0.32"
|
version = "1.0.33"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "ba508cc11742c0dc5c1659771673afbab7a0efab23aa17e854cbab0837ed0b43"
|
checksum = "fb37d2df5df740e582f28f8560cf425f52bb267d872fe58358eadb554909f07a"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"unicode-xid",
|
"unicode-xid",
|
||||||
]
|
]
|
||||||
|
@ -826,12 +783,6 @@ dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "radium"
|
|
||||||
version = "0.5.3"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "941ba9d78d8e2f7ce474c015eea4d9c6d25b6a3327f9832ee29a4de27f91bbb8"
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "rand"
|
name = "rand"
|
||||||
version = "0.8.4"
|
version = "0.8.4"
|
||||||
|
@ -909,9 +860,9 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "rusqlite"
|
name = "rusqlite"
|
||||||
version = "0.26.1"
|
version = "0.26.3"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "8a82b0b91fad72160c56bf8da7a549b25d7c31109f52cc1437eac4c0ad2550a7"
|
checksum = "4ba4d3462c8b2e4d7f4fcfcf2b296dc6b65404fbbc7b63daa37fd485c149daf7"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bitflags",
|
"bitflags",
|
||||||
"fallible-iterator",
|
"fallible-iterator",
|
||||||
|
@ -922,17 +873,11 @@ dependencies = [
|
||||||
"smallvec",
|
"smallvec",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "rustc-hash"
|
|
||||||
version = "1.1.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2"
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "ryu"
|
name = "ryu"
|
||||||
version = "1.0.5"
|
version = "1.0.9"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "71d301d4193d031abdd79ff7e3dd721168a9572ef3fe51a1517aba235bd8f86e"
|
checksum = "73b4b750c782965c211b42f022f59af1fbceabdd026623714f104152f1ec149f"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "scopeguard"
|
name = "scopeguard"
|
||||||
|
@ -942,18 +887,18 @@ checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "serde"
|
name = "serde"
|
||||||
version = "1.0.130"
|
version = "1.0.131"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "f12d06de37cf59146fbdecab66aa99f9fe4f78722e3607577a5375d66bd0c913"
|
checksum = "b4ad69dfbd3e45369132cc64e6748c2d65cdfb001a2b1c232d128b4ad60561c1"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"serde_derive",
|
"serde_derive",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "serde_derive"
|
name = "serde_derive"
|
||||||
version = "1.0.130"
|
version = "1.0.131"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "d7bc1a1ab1961464eae040d96713baa5a724a8152c1222492465b54322ec508b"
|
checksum = "b710a83c4e0dff6a3d511946b95274ad9ca9e5d3ae497b63fda866ac955358d2"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
|
@ -962,11 +907,11 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "serde_json"
|
name = "serde_json"
|
||||||
version = "1.0.71"
|
version = "1.0.73"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "063bf466a64011ac24040a49009724ee60a57da1b437617ceb32e53ad61bfb19"
|
checksum = "bcbd0344bc6533bc7ec56df11d42fb70f1b912351c0825ccb7211b59d8af7cf5"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"itoa",
|
"itoa 1.0.1",
|
||||||
"ryu",
|
"ryu",
|
||||||
"serde",
|
"serde",
|
||||||
]
|
]
|
||||||
|
@ -1019,6 +964,7 @@ dependencies = [
|
||||||
"log",
|
"log",
|
||||||
"netlink-packet-core",
|
"netlink-packet-core",
|
||||||
"netlink-packet-route",
|
"netlink-packet-route",
|
||||||
|
"netlink-request",
|
||||||
"netlink-sys",
|
"netlink-sys",
|
||||||
"nix",
|
"nix",
|
||||||
"publicip",
|
"publicip",
|
||||||
|
@ -1028,15 +974,8 @@ dependencies = [
|
||||||
"toml",
|
"toml",
|
||||||
"url",
|
"url",
|
||||||
"wireguard-control",
|
"wireguard-control",
|
||||||
"wireguard-control-sys",
|
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "shlex"
|
|
||||||
version = "1.1.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "43b2853a4d09f215c24cc5489c992ce46052d359b5109343cbafbf26bc62f8a3"
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "smallvec"
|
name = "smallvec"
|
||||||
version = "1.7.0"
|
version = "1.7.0"
|
||||||
|
@ -1091,21 +1030,15 @@ checksum = "6bdef32e8150c2a081110b42772ffe7d7c9032b606bc226c8260fd97e0976601"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "syn"
|
name = "syn"
|
||||||
version = "1.0.81"
|
version = "1.0.82"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "f2afee18b8beb5a596ecb4a2dce128c719b4ba399d34126b9e4396e3f9860966"
|
checksum = "8daf5dd0bb60cbd4137b1b587d2fc0ae729bc07cf01cd70b36a1ed5ade3b9d59"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
"unicode-xid",
|
"unicode-xid",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "tap"
|
|
||||||
version = "1.0.1"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "55937e1799185b12863d447f42597ed69d9928686b8d88a1df17376a097d8369"
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "tempfile"
|
name = "tempfile"
|
||||||
version = "3.2.0"
|
version = "3.2.0"
|
||||||
|
@ -1395,25 +1328,15 @@ dependencies = [
|
||||||
"curve25519-dalek",
|
"curve25519-dalek",
|
||||||
"hex",
|
"hex",
|
||||||
"libc",
|
"libc",
|
||||||
|
"netlink-packet-core",
|
||||||
|
"netlink-packet-generic",
|
||||||
|
"netlink-packet-route",
|
||||||
|
"netlink-packet-wireguard",
|
||||||
|
"netlink-request",
|
||||||
|
"netlink-sys",
|
||||||
"rand_core",
|
"rand_core",
|
||||||
"wireguard-control-sys",
|
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "wireguard-control-sys"
|
|
||||||
version = "1.5.2"
|
|
||||||
dependencies = [
|
|
||||||
"bindgen",
|
|
||||||
"cc",
|
|
||||||
"libc",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "wyz"
|
|
||||||
version = "0.2.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "85e60b0d1b5f99db2556934e21937020776a5d31520bf169e851ac44e6420214"
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "zeroize"
|
name = "zeroize"
|
||||||
version = "1.4.3"
|
version = "1.4.3"
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
[workspace]
|
[workspace]
|
||||||
members = ["server", "client", "hostsfile", "shared", "publicip"]
|
members = ["server", "client", "hostsfile", "shared", "publicip", "netlink-request"]
|
||||||
|
|
||||||
[profile.release]
|
[profile.release]
|
||||||
codegen-units = 1
|
codegen-units = 1
|
||||||
|
|
|
@ -522,7 +522,10 @@ fn fetch(
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
log::info!("bringing up interface {}.", interface.as_str_lossy().yellow());
|
log::info!(
|
||||||
|
"bringing up interface {}.",
|
||||||
|
interface.as_str_lossy().yellow()
|
||||||
|
);
|
||||||
let resolved_endpoint = config
|
let resolved_endpoint = config
|
||||||
.server
|
.server
|
||||||
.external_endpoint
|
.external_endpoint
|
||||||
|
@ -543,7 +546,10 @@ fn fetch(
|
||||||
.with_str(interface.to_string())?;
|
.with_str(interface.to_string())?;
|
||||||
}
|
}
|
||||||
|
|
||||||
log::info!("fetching state for {} from server...", interface.as_str_lossy().yellow());
|
log::info!(
|
||||||
|
"fetching state for {} from server...",
|
||||||
|
interface.as_str_lossy().yellow()
|
||||||
|
);
|
||||||
let mut store = DataStore::open_or_create(&opts.data_dir, interface)?;
|
let mut store = DataStore::open_or_create(&opts.data_dir, interface)?;
|
||||||
let api = Api::new(&config.server);
|
let api = Api::new(&config.server);
|
||||||
let State { peers, cidrs } = api.http("GET", "/user/state")?;
|
let State { peers, cidrs } = api.http("GET", "/user/state")?;
|
||||||
|
@ -978,11 +984,22 @@ fn show(opts: &Opts, short: bool, tree: bool, interface: Option<Interface>) -> R
|
||||||
}
|
}
|
||||||
|
|
||||||
for (device_info, store) in devices {
|
for (device_info, store) in devices {
|
||||||
|
let public_key = match &device_info.public_key {
|
||||||
|
Some(key) => key.to_base64(),
|
||||||
|
None => {
|
||||||
|
log::warn!(
|
||||||
|
"network {} is missing public key.",
|
||||||
|
device_info.name.to_string().yellow()
|
||||||
|
);
|
||||||
|
continue;
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
let peers = store.peers();
|
let peers = store.peers();
|
||||||
let cidrs = store.cidrs();
|
let cidrs = store.cidrs();
|
||||||
let me = peers
|
let me = peers
|
||||||
.iter()
|
.iter()
|
||||||
.find(|p| p.public_key == device_info.public_key.as_ref().unwrap().to_base64())
|
.find(|p| p.public_key == public_key)
|
||||||
.ok_or_else(|| anyhow!("missing peer info"))?;
|
.ok_or_else(|| anyhow!("missing peer info"))?;
|
||||||
|
|
||||||
let mut peer_states = device_info
|
let mut peer_states = device_info
|
||||||
|
|
|
@ -3,8 +3,8 @@ use colored::*;
|
||||||
use indoc::eprintdoc;
|
use indoc::eprintdoc;
|
||||||
use log::{Level, LevelFilter};
|
use log::{Level, LevelFilter};
|
||||||
use serde::{de::DeserializeOwned, Serialize};
|
use serde::{de::DeserializeOwned, Serialize};
|
||||||
use shared::{interface_config::ServerInfo, PeerDiff, INNERNET_PUBKEY_HEADER, Interface};
|
use shared::{interface_config::ServerInfo, Interface, PeerDiff, INNERNET_PUBKEY_HEADER};
|
||||||
use std::{io, path::Path, time::Duration, ffi::OsStr};
|
use std::{ffi::OsStr, io, path::Path, time::Duration};
|
||||||
use ureq::{Agent, AgentBuilder};
|
use ureq::{Agent, AgentBuilder};
|
||||||
|
|
||||||
static LOGGER: Logger = Logger;
|
static LOGGER: Logger = Logger;
|
||||||
|
@ -176,18 +176,19 @@ pub fn all_installed(config_dir: &Path) -> Result<Vec<Interface>, std::io::Error
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.collect::<Result<_, _>>()?;
|
.collect::<Result<_, _>>()?;
|
||||||
|
|
||||||
let installed: Vec<_> = entries.into_iter()
|
let installed: Vec<_> = entries
|
||||||
|
.into_iter()
|
||||||
.filter(|entry| match entry.file_type() {
|
.filter(|entry| match entry.file_type() {
|
||||||
Ok(f) => f.is_file(),
|
Ok(f) => f.is_file(),
|
||||||
_ => false
|
_ => false,
|
||||||
})
|
})
|
||||||
.filter_map(|entry| {
|
.filter_map(|entry| {
|
||||||
let path = entry.path();
|
let path = entry.path();
|
||||||
match (path.extension(), path.file_stem()) {
|
match (path.extension(), path.file_stem()) {
|
||||||
(Some(extension), Some(stem)) if extension == OsStr::new("conf") => {
|
(Some(extension), Some(stem)) if extension == OsStr::new("conf") => {
|
||||||
Some(stem.to_string_lossy().to_string())
|
Some(stem.to_string_lossy().to_string())
|
||||||
}
|
},
|
||||||
_ => None
|
_ => None,
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.map(|name| name.parse())
|
.map(|name| name.parse())
|
||||||
|
|
|
@ -0,0 +1,11 @@
|
||||||
|
[package]
|
||||||
|
name = "netlink-request"
|
||||||
|
version = "0.1.0"
|
||||||
|
edition = "2021"
|
||||||
|
|
||||||
|
[target.'cfg(target_os = "linux")'.dependencies]
|
||||||
|
netlink-sys = "0.8"
|
||||||
|
netlink-packet-core = "0.4"
|
||||||
|
netlink-packet-generic = "0.3"
|
||||||
|
netlink-packet-route = "0.10"
|
||||||
|
netlink-packet-wireguard = { git = "https://github.com/mcginty/netlink", branch = "wireguard-fixes" }
|
|
@ -0,0 +1,125 @@
|
||||||
|
#[cfg(target_os = "linux")]
|
||||||
|
mod linux {
|
||||||
|
use netlink_packet_core::{
|
||||||
|
NetlinkDeserializable, NetlinkMessage, NetlinkPayload, NetlinkSerializable, NLM_F_ACK,
|
||||||
|
NLM_F_CREATE, NLM_F_EXCL, NLM_F_REQUEST,
|
||||||
|
};
|
||||||
|
use netlink_packet_generic::{
|
||||||
|
ctrl::{nlas::GenlCtrlAttrs, GenlCtrl, GenlCtrlCmd},
|
||||||
|
GenlFamily, GenlMessage,
|
||||||
|
};
|
||||||
|
use netlink_packet_route::RtnlMessage;
|
||||||
|
use netlink_sys::{constants::NETLINK_GENERIC, protocols::NETLINK_ROUTE, Socket};
|
||||||
|
use std::{fmt::Debug, io};
|
||||||
|
|
||||||
|
macro_rules! get_nla_value {
|
||||||
|
($nlas:expr, $e:ident, $v:ident) => {
|
||||||
|
$nlas.iter().find_map(|attr| match attr {
|
||||||
|
$e::$v(value) => Some(value),
|
||||||
|
_ => None,
|
||||||
|
})
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn netlink_request_genl<F>(
|
||||||
|
mut message: GenlMessage<F>,
|
||||||
|
flags: Option<u16>,
|
||||||
|
) -> Result<Vec<NetlinkMessage<GenlMessage<F>>>, io::Error>
|
||||||
|
where
|
||||||
|
F: GenlFamily + Clone + Debug + Eq,
|
||||||
|
GenlMessage<F>: Clone + Debug + Eq + NetlinkSerializable + NetlinkDeserializable,
|
||||||
|
{
|
||||||
|
if message.family_id() == 0 {
|
||||||
|
let genlmsg: GenlMessage<GenlCtrl> = GenlMessage::from_payload(GenlCtrl {
|
||||||
|
cmd: GenlCtrlCmd::GetFamily,
|
||||||
|
nlas: vec![GenlCtrlAttrs::FamilyName(F::family_name().to_string())],
|
||||||
|
});
|
||||||
|
let responses =
|
||||||
|
netlink_request_genl::<GenlCtrl>(genlmsg, Some(NLM_F_REQUEST | NLM_F_ACK))?;
|
||||||
|
|
||||||
|
match responses.get(0) {
|
||||||
|
Some(NetlinkMessage {
|
||||||
|
payload:
|
||||||
|
NetlinkPayload::InnerMessage(GenlMessage {
|
||||||
|
payload: GenlCtrl { nlas, .. },
|
||||||
|
..
|
||||||
|
}),
|
||||||
|
..
|
||||||
|
}) => {
|
||||||
|
let family_id = get_nla_value!(nlas, GenlCtrlAttrs, FamilyId)
|
||||||
|
.ok_or_else(|| io::ErrorKind::NotFound)?;
|
||||||
|
message.set_resolved_family_id(*family_id);
|
||||||
|
},
|
||||||
|
_ => {
|
||||||
|
return Err(io::Error::new(
|
||||||
|
io::ErrorKind::InvalidData,
|
||||||
|
"Unexpected netlink payload",
|
||||||
|
))
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
netlink_request(message, flags, NETLINK_GENERIC)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn netlink_request_rtnl(
|
||||||
|
message: RtnlMessage,
|
||||||
|
flags: Option<u16>,
|
||||||
|
) -> Result<Vec<NetlinkMessage<RtnlMessage>>, io::Error> {
|
||||||
|
netlink_request(message, flags, NETLINK_ROUTE)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn netlink_request<I>(
|
||||||
|
message: I,
|
||||||
|
flags: Option<u16>,
|
||||||
|
socket: isize,
|
||||||
|
) -> Result<Vec<NetlinkMessage<I>>, io::Error>
|
||||||
|
where
|
||||||
|
NetlinkPayload<I>: From<I>,
|
||||||
|
I: Clone + Debug + Eq + NetlinkSerializable + NetlinkDeserializable,
|
||||||
|
{
|
||||||
|
let mut req = NetlinkMessage::from(message);
|
||||||
|
req.header.flags = flags.unwrap_or(NLM_F_REQUEST | NLM_F_ACK | NLM_F_EXCL | NLM_F_CREATE);
|
||||||
|
req.finalize();
|
||||||
|
let mut buf = [0; 4096];
|
||||||
|
req.serialize(&mut buf);
|
||||||
|
let len = req.buffer_len();
|
||||||
|
|
||||||
|
let socket = Socket::new(socket)?;
|
||||||
|
let kernel_addr = netlink_sys::SocketAddr::new(0, 0);
|
||||||
|
socket.connect(&kernel_addr)?;
|
||||||
|
let n_sent = socket.send(&buf[..len], 0)?;
|
||||||
|
if n_sent != len {
|
||||||
|
return Err(io::Error::new(
|
||||||
|
io::ErrorKind::UnexpectedEof,
|
||||||
|
"failed to send netlink request",
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut responses = vec![];
|
||||||
|
loop {
|
||||||
|
let n_received = socket.recv(&mut &mut buf[..], 0)?;
|
||||||
|
let mut offset = 0;
|
||||||
|
loop {
|
||||||
|
let bytes = &buf[offset..];
|
||||||
|
let response = NetlinkMessage::<I>::deserialize(bytes)
|
||||||
|
.map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e))?;
|
||||||
|
responses.push(response.clone());
|
||||||
|
match response.payload {
|
||||||
|
// We've parsed all parts of the response and can leave the loop.
|
||||||
|
NetlinkPayload::Ack(_) | NetlinkPayload::Done => return Ok(responses),
|
||||||
|
NetlinkPayload::Error(e) => return Err(e.into()),
|
||||||
|
_ => {},
|
||||||
|
}
|
||||||
|
offset += response.header.length as usize;
|
||||||
|
if offset == n_received || response.header.length == 0 {
|
||||||
|
// We've fully parsed the datagram, but there may be further datagrams
|
||||||
|
// with additional netlink response parts.
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(target_os = "linux")]
|
||||||
|
pub use linux::{netlink_request, netlink_request_genl, netlink_request_rtnl};
|
|
@ -25,10 +25,10 @@ url = "2"
|
||||||
wireguard-control = { path = "../wireguard-control" }
|
wireguard-control = { path = "../wireguard-control" }
|
||||||
|
|
||||||
[target.'cfg(target_os = "linux")'.dependencies]
|
[target.'cfg(target_os = "linux")'.dependencies]
|
||||||
netlink-sys = "0.7"
|
netlink-sys = "0.8"
|
||||||
netlink-packet-core = "0.2"
|
netlink-packet-core = "0.4"
|
||||||
netlink-packet-route = "0.8"
|
netlink-packet-route = "0.10"
|
||||||
wireguard-control-sys = { path = "../wireguard-control-sys" }
|
netlink-request = { path = "../netlink-request" }
|
||||||
|
|
||||||
[target.'cfg(target_os = "macos")'.dependencies]
|
[target.'cfg(target_os = "macos")'.dependencies]
|
||||||
nix = "0.23"
|
nix = "0.23"
|
||||||
|
|
|
@ -1,7 +1,5 @@
|
||||||
use ipnetwork::IpNetwork;
|
use ipnetwork::IpNetwork;
|
||||||
use netlink_packet_core::{
|
use netlink_packet_core::{NetlinkMessage, NetlinkPayload, NLM_F_ACK, NLM_F_CREATE, NLM_F_REQUEST};
|
||||||
NetlinkMessage, NetlinkPayload, NLM_F_ACK, NLM_F_CREATE, NLM_F_EXCL, NLM_F_REQUEST,
|
|
||||||
};
|
|
||||||
use netlink_packet_route::{
|
use netlink_packet_route::{
|
||||||
address,
|
address,
|
||||||
constants::*,
|
constants::*,
|
||||||
|
@ -9,7 +7,7 @@ use netlink_packet_route::{
|
||||||
route, AddressHeader, AddressMessage, LinkHeader, LinkMessage, RouteHeader, RouteMessage,
|
route, AddressHeader, AddressMessage, LinkHeader, LinkMessage, RouteHeader, RouteMessage,
|
||||||
RtnlMessage, RTN_UNICAST, RT_SCOPE_LINK, RT_TABLE_MAIN,
|
RtnlMessage, RTN_UNICAST, RT_SCOPE_LINK, RT_TABLE_MAIN,
|
||||||
};
|
};
|
||||||
use netlink_sys::{protocols::NETLINK_ROUTE, Socket, SocketAddr};
|
use netlink_request::netlink_request_rtnl;
|
||||||
use std::{io, net::IpAddr};
|
use std::{io, net::IpAddr};
|
||||||
use wireguard_control::InterfaceName;
|
use wireguard_control::InterfaceName;
|
||||||
|
|
||||||
|
@ -23,55 +21,6 @@ fn if_nametoindex(interface: &InterfaceName) -> Result<u32, io::Error> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn netlink_call(
|
|
||||||
message: RtnlMessage,
|
|
||||||
flags: Option<u16>,
|
|
||||||
) -> Result<Vec<NetlinkMessage<RtnlMessage>>, io::Error> {
|
|
||||||
let mut req = NetlinkMessage::from(message);
|
|
||||||
req.header.flags = flags.unwrap_or(NLM_F_REQUEST | NLM_F_ACK | NLM_F_EXCL | NLM_F_CREATE);
|
|
||||||
req.finalize();
|
|
||||||
let mut buf = [0; 4096];
|
|
||||||
req.serialize(&mut buf);
|
|
||||||
let len = req.buffer_len();
|
|
||||||
|
|
||||||
log::trace!("netlink request: {:?}", req);
|
|
||||||
let socket = Socket::new(NETLINK_ROUTE)?;
|
|
||||||
let kernel_addr = SocketAddr::new(0, 0);
|
|
||||||
socket.connect(&kernel_addr)?;
|
|
||||||
let n_sent = socket.send(&buf[..len], 0)?;
|
|
||||||
if n_sent != len {
|
|
||||||
return Err(io::Error::new(
|
|
||||||
io::ErrorKind::UnexpectedEof,
|
|
||||||
"failed to send netlink request",
|
|
||||||
));
|
|
||||||
}
|
|
||||||
|
|
||||||
let mut responses = vec![];
|
|
||||||
loop {
|
|
||||||
let n_received = socket.recv(&mut buf[..], 0)?;
|
|
||||||
let mut offset = 0;
|
|
||||||
loop {
|
|
||||||
let bytes = &buf[offset..];
|
|
||||||
let response = NetlinkMessage::<RtnlMessage>::deserialize(bytes)
|
|
||||||
.map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e))?;
|
|
||||||
responses.push(response.clone());
|
|
||||||
log::trace!("netlink response: {:?}", response);
|
|
||||||
match response.payload {
|
|
||||||
// We've parsed all parts of the response and can leave the loop.
|
|
||||||
NetlinkPayload::Ack(_) | NetlinkPayload::Done => return Ok(responses),
|
|
||||||
NetlinkPayload::Error(e) => return Err(e.into()),
|
|
||||||
_ => {},
|
|
||||||
}
|
|
||||||
offset += response.header.length as usize;
|
|
||||||
if offset == n_received || response.header.length == 0 {
|
|
||||||
// We've fully parsed the datagram, but there may be further datagrams
|
|
||||||
// with additional netlink response parts.
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn set_up(interface: &InterfaceName, mtu: u32) -> Result<(), io::Error> {
|
pub fn set_up(interface: &InterfaceName, mtu: u32) -> Result<(), io::Error> {
|
||||||
let index = if_nametoindex(interface)?;
|
let index = if_nametoindex(interface)?;
|
||||||
let message = LinkMessage {
|
let message = LinkMessage {
|
||||||
|
@ -82,7 +31,8 @@ pub fn set_up(interface: &InterfaceName, mtu: u32) -> Result<(), io::Error> {
|
||||||
},
|
},
|
||||||
nlas: vec![link::nlas::Nla::Mtu(mtu)],
|
nlas: vec![link::nlas::Nla::Mtu(mtu)],
|
||||||
};
|
};
|
||||||
netlink_call(RtnlMessage::SetLink(message), None)?;
|
netlink_request_rtnl(RtnlMessage::SetLink(message), None)?;
|
||||||
|
log::debug!("set interface {} up with mtu {}", interface, mtu);
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -114,10 +64,11 @@ pub fn set_addr(interface: &InterfaceName, addr: IpNetwork) -> Result<(), io::Er
|
||||||
},
|
},
|
||||||
nlas,
|
nlas,
|
||||||
};
|
};
|
||||||
netlink_call(
|
netlink_request_rtnl(
|
||||||
RtnlMessage::NewAddress(message),
|
RtnlMessage::NewAddress(message),
|
||||||
Some(NLM_F_REQUEST | NLM_F_ACK | NLM_F_REPLACE | NLM_F_CREATE),
|
Some(NLM_F_REQUEST | NLM_F_ACK | NLM_F_REPLACE | NLM_F_CREATE),
|
||||||
)?;
|
)?;
|
||||||
|
log::debug!("set address {} on interface {}", addr, interface);
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -140,15 +91,21 @@ pub fn add_route(interface: &InterfaceName, cidr: IpNetwork) -> Result<bool, io:
|
||||||
nlas: vec![route::Nla::Destination(dst), route::Nla::Oif(if_index)],
|
nlas: vec![route::Nla::Destination(dst), route::Nla::Oif(if_index)],
|
||||||
};
|
};
|
||||||
|
|
||||||
match netlink_call(RtnlMessage::NewRoute(message), None) {
|
match netlink_request_rtnl(RtnlMessage::NewRoute(message), None) {
|
||||||
Ok(_) => Ok(true),
|
Ok(_) => {
|
||||||
Err(e) if e.kind() == io::ErrorKind::AlreadyExists => Ok(false),
|
log::debug!("added route {} to interface {}", cidr, interface);
|
||||||
|
Ok(true)
|
||||||
|
},
|
||||||
|
Err(e) if e.kind() == io::ErrorKind::AlreadyExists => {
|
||||||
|
log::debug!("route {} already existed.", cidr);
|
||||||
|
Ok(false)
|
||||||
|
},
|
||||||
Err(e) => Err(e),
|
Err(e) => Err(e),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_links() -> Result<Vec<String>, io::Error> {
|
fn get_links() -> Result<Vec<String>, io::Error> {
|
||||||
let link_responses = netlink_call(
|
let link_responses = netlink_request_rtnl(
|
||||||
RtnlMessage::GetLink(LinkMessage::default()),
|
RtnlMessage::GetLink(LinkMessage::default()),
|
||||||
Some(NLM_F_DUMP | NLM_F_REQUEST),
|
Some(NLM_F_DUMP | NLM_F_REQUEST),
|
||||||
)?;
|
)?;
|
||||||
|
@ -181,7 +138,7 @@ fn get_links() -> Result<Vec<String>, io::Error> {
|
||||||
|
|
||||||
pub fn get_local_addrs() -> Result<impl Iterator<Item = IpAddr>, io::Error> {
|
pub fn get_local_addrs() -> Result<impl Iterator<Item = IpAddr>, io::Error> {
|
||||||
let links = get_links()?;
|
let links = get_links()?;
|
||||||
let addr_responses = netlink_call(
|
let addr_responses = netlink_request_rtnl(
|
||||||
RtnlMessage::GetAddress(AddressMessage::default()),
|
RtnlMessage::GetAddress(AddressMessage::default()),
|
||||||
Some(NLM_F_DUMP | NLM_F_REQUEST),
|
Some(NLM_F_DUMP | NLM_F_REQUEST),
|
||||||
)?;
|
)?;
|
||||||
|
|
|
@ -1,3 +0,0 @@
|
||||||
/target
|
|
||||||
**/*.rs.bk
|
|
||||||
Cargo.lock
|
|
|
@ -1,19 +0,0 @@
|
||||||
[package]
|
|
||||||
authors = ["K900 <me@0upti.me>", "Jake McGinty <jake@tonari.no>"]
|
|
||||||
categories = ["external-ffi-bindings", "os::unix-apis"]
|
|
||||||
description = "Raw bindings to the WireGuard embeddable C library"
|
|
||||||
license = "LGPL-2.1-or-later"
|
|
||||||
name = "wireguard-control-sys"
|
|
||||||
readme = "README.md"
|
|
||||||
repository = "https://github.com/tonarino/innernet"
|
|
||||||
version = "1.5.2"
|
|
||||||
|
|
||||||
[dependencies]
|
|
||||||
libc = "0.2"
|
|
||||||
|
|
||||||
[features]
|
|
||||||
buildtime_bindgen = ["bindgen"]
|
|
||||||
|
|
||||||
[build-dependencies]
|
|
||||||
bindgen = { version = "0", default-features = false, optional = true }
|
|
||||||
cc = "1.0"
|
|
|
@ -1,5 +0,0 @@
|
||||||
# `wireguard-control-sys`
|
|
||||||
|
|
||||||
A low-level FFI around the [`embaddable-wg-library`](https://git.zx2c4.com/wireguard-tools/tree/contrib/embeddable-wg-library) WireGuard C library, which in turn communicates with the Linux kernel WireGuard via Netlink.
|
|
||||||
|
|
||||||
You *probably* want to use the [`wireguard-control`](https://crates.io/crates/wireguard-control) crate instead.
|
|
|
@ -1,968 +0,0 @@
|
||||||
/* automatically generated by rust-bindgen 0.59.1 */
|
|
||||||
|
|
||||||
pub type __uint8_t = ::std::os::raw::c_uchar;
|
|
||||||
pub type __uint16_t = ::std::os::raw::c_ushort;
|
|
||||||
pub type __uint32_t = ::std::os::raw::c_uint;
|
|
||||||
pub type __int64_t = ::std::os::raw::c_long;
|
|
||||||
pub type __uint64_t = ::std::os::raw::c_ulong;
|
|
||||||
pub type sa_family_t = ::std::os::raw::c_ushort;
|
|
||||||
#[repr(C)]
|
|
||||||
#[derive(Debug, Default, Copy, Clone)]
|
|
||||||
pub struct sockaddr {
|
|
||||||
pub sa_family: sa_family_t,
|
|
||||||
pub sa_data: [::std::os::raw::c_char; 14usize],
|
|
||||||
}
|
|
||||||
#[test]
|
|
||||||
fn bindgen_test_layout_sockaddr() {
|
|
||||||
assert_eq!(
|
|
||||||
::std::mem::size_of::<sockaddr>(),
|
|
||||||
16usize,
|
|
||||||
concat!("Size of: ", stringify!(sockaddr))
|
|
||||||
);
|
|
||||||
assert_eq!(
|
|
||||||
::std::mem::align_of::<sockaddr>(),
|
|
||||||
2usize,
|
|
||||||
concat!("Alignment of ", stringify!(sockaddr))
|
|
||||||
);
|
|
||||||
assert_eq!(
|
|
||||||
unsafe { &(*(::std::ptr::null::<sockaddr>())).sa_family as *const _ as usize },
|
|
||||||
0usize,
|
|
||||||
concat!(
|
|
||||||
"Offset of field: ",
|
|
||||||
stringify!(sockaddr),
|
|
||||||
"::",
|
|
||||||
stringify!(sa_family)
|
|
||||||
)
|
|
||||||
);
|
|
||||||
assert_eq!(
|
|
||||||
unsafe { &(*(::std::ptr::null::<sockaddr>())).sa_data as *const _ as usize },
|
|
||||||
2usize,
|
|
||||||
concat!(
|
|
||||||
"Offset of field: ",
|
|
||||||
stringify!(sockaddr),
|
|
||||||
"::",
|
|
||||||
stringify!(sa_data)
|
|
||||||
)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
pub type in_addr_t = u32;
|
|
||||||
#[repr(C)]
|
|
||||||
#[derive(Debug, Default, Copy, Clone)]
|
|
||||||
pub struct in_addr {
|
|
||||||
pub s_addr: in_addr_t,
|
|
||||||
}
|
|
||||||
#[test]
|
|
||||||
fn bindgen_test_layout_in_addr() {
|
|
||||||
assert_eq!(
|
|
||||||
::std::mem::size_of::<in_addr>(),
|
|
||||||
4usize,
|
|
||||||
concat!("Size of: ", stringify!(in_addr))
|
|
||||||
);
|
|
||||||
assert_eq!(
|
|
||||||
::std::mem::align_of::<in_addr>(),
|
|
||||||
4usize,
|
|
||||||
concat!("Alignment of ", stringify!(in_addr))
|
|
||||||
);
|
|
||||||
assert_eq!(
|
|
||||||
unsafe { &(*(::std::ptr::null::<in_addr>())).s_addr as *const _ as usize },
|
|
||||||
0usize,
|
|
||||||
concat!(
|
|
||||||
"Offset of field: ",
|
|
||||||
stringify!(in_addr),
|
|
||||||
"::",
|
|
||||||
stringify!(s_addr)
|
|
||||||
)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
pub type in_port_t = u16;
|
|
||||||
#[repr(C)]
|
|
||||||
#[derive(Copy, Clone)]
|
|
||||||
pub struct in6_addr {
|
|
||||||
pub __in6_u: in6_addr__bindgen_ty_1,
|
|
||||||
}
|
|
||||||
#[repr(C)]
|
|
||||||
#[derive(Copy, Clone)]
|
|
||||||
pub union in6_addr__bindgen_ty_1 {
|
|
||||||
pub __u6_addr8: [u8; 16usize],
|
|
||||||
pub __u6_addr16: [u16; 8usize],
|
|
||||||
pub __u6_addr32: [u32; 4usize],
|
|
||||||
}
|
|
||||||
#[test]
|
|
||||||
fn bindgen_test_layout_in6_addr__bindgen_ty_1() {
|
|
||||||
assert_eq!(
|
|
||||||
::std::mem::size_of::<in6_addr__bindgen_ty_1>(),
|
|
||||||
16usize,
|
|
||||||
concat!("Size of: ", stringify!(in6_addr__bindgen_ty_1))
|
|
||||||
);
|
|
||||||
assert_eq!(
|
|
||||||
::std::mem::align_of::<in6_addr__bindgen_ty_1>(),
|
|
||||||
4usize,
|
|
||||||
concat!("Alignment of ", stringify!(in6_addr__bindgen_ty_1))
|
|
||||||
);
|
|
||||||
assert_eq!(
|
|
||||||
unsafe {
|
|
||||||
&(*(::std::ptr::null::<in6_addr__bindgen_ty_1>())).__u6_addr8 as *const _ as usize
|
|
||||||
},
|
|
||||||
0usize,
|
|
||||||
concat!(
|
|
||||||
"Offset of field: ",
|
|
||||||
stringify!(in6_addr__bindgen_ty_1),
|
|
||||||
"::",
|
|
||||||
stringify!(__u6_addr8)
|
|
||||||
)
|
|
||||||
);
|
|
||||||
assert_eq!(
|
|
||||||
unsafe {
|
|
||||||
&(*(::std::ptr::null::<in6_addr__bindgen_ty_1>())).__u6_addr16 as *const _ as usize
|
|
||||||
},
|
|
||||||
0usize,
|
|
||||||
concat!(
|
|
||||||
"Offset of field: ",
|
|
||||||
stringify!(in6_addr__bindgen_ty_1),
|
|
||||||
"::",
|
|
||||||
stringify!(__u6_addr16)
|
|
||||||
)
|
|
||||||
);
|
|
||||||
assert_eq!(
|
|
||||||
unsafe {
|
|
||||||
&(*(::std::ptr::null::<in6_addr__bindgen_ty_1>())).__u6_addr32 as *const _ as usize
|
|
||||||
},
|
|
||||||
0usize,
|
|
||||||
concat!(
|
|
||||||
"Offset of field: ",
|
|
||||||
stringify!(in6_addr__bindgen_ty_1),
|
|
||||||
"::",
|
|
||||||
stringify!(__u6_addr32)
|
|
||||||
)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
impl Default for in6_addr__bindgen_ty_1 {
|
|
||||||
fn default() -> Self {
|
|
||||||
let mut s = ::std::mem::MaybeUninit::<Self>::uninit();
|
|
||||||
unsafe {
|
|
||||||
::std::ptr::write_bytes(s.as_mut_ptr(), 0, 1);
|
|
||||||
s.assume_init()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
impl ::std::fmt::Debug for in6_addr__bindgen_ty_1 {
|
|
||||||
fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result {
|
|
||||||
write!(f, "in6_addr__bindgen_ty_1 {{ union }}")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
#[test]
|
|
||||||
fn bindgen_test_layout_in6_addr() {
|
|
||||||
assert_eq!(
|
|
||||||
::std::mem::size_of::<in6_addr>(),
|
|
||||||
16usize,
|
|
||||||
concat!("Size of: ", stringify!(in6_addr))
|
|
||||||
);
|
|
||||||
assert_eq!(
|
|
||||||
::std::mem::align_of::<in6_addr>(),
|
|
||||||
4usize,
|
|
||||||
concat!("Alignment of ", stringify!(in6_addr))
|
|
||||||
);
|
|
||||||
assert_eq!(
|
|
||||||
unsafe { &(*(::std::ptr::null::<in6_addr>())).__in6_u as *const _ as usize },
|
|
||||||
0usize,
|
|
||||||
concat!(
|
|
||||||
"Offset of field: ",
|
|
||||||
stringify!(in6_addr),
|
|
||||||
"::",
|
|
||||||
stringify!(__in6_u)
|
|
||||||
)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
impl Default for in6_addr {
|
|
||||||
fn default() -> Self {
|
|
||||||
let mut s = ::std::mem::MaybeUninit::<Self>::uninit();
|
|
||||||
unsafe {
|
|
||||||
::std::ptr::write_bytes(s.as_mut_ptr(), 0, 1);
|
|
||||||
s.assume_init()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
impl ::std::fmt::Debug for in6_addr {
|
|
||||||
fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result {
|
|
||||||
write!(f, "in6_addr {{ __in6_u: {:?} }}", self.__in6_u)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
#[repr(C)]
|
|
||||||
#[derive(Debug, Default, Copy, Clone)]
|
|
||||||
pub struct sockaddr_in {
|
|
||||||
pub sin_family: sa_family_t,
|
|
||||||
pub sin_port: in_port_t,
|
|
||||||
pub sin_addr: in_addr,
|
|
||||||
pub sin_zero: [::std::os::raw::c_uchar; 8usize],
|
|
||||||
}
|
|
||||||
#[test]
|
|
||||||
fn bindgen_test_layout_sockaddr_in() {
|
|
||||||
assert_eq!(
|
|
||||||
::std::mem::size_of::<sockaddr_in>(),
|
|
||||||
16usize,
|
|
||||||
concat!("Size of: ", stringify!(sockaddr_in))
|
|
||||||
);
|
|
||||||
assert_eq!(
|
|
||||||
::std::mem::align_of::<sockaddr_in>(),
|
|
||||||
4usize,
|
|
||||||
concat!("Alignment of ", stringify!(sockaddr_in))
|
|
||||||
);
|
|
||||||
assert_eq!(
|
|
||||||
unsafe { &(*(::std::ptr::null::<sockaddr_in>())).sin_family as *const _ as usize },
|
|
||||||
0usize,
|
|
||||||
concat!(
|
|
||||||
"Offset of field: ",
|
|
||||||
stringify!(sockaddr_in),
|
|
||||||
"::",
|
|
||||||
stringify!(sin_family)
|
|
||||||
)
|
|
||||||
);
|
|
||||||
assert_eq!(
|
|
||||||
unsafe { &(*(::std::ptr::null::<sockaddr_in>())).sin_port as *const _ as usize },
|
|
||||||
2usize,
|
|
||||||
concat!(
|
|
||||||
"Offset of field: ",
|
|
||||||
stringify!(sockaddr_in),
|
|
||||||
"::",
|
|
||||||
stringify!(sin_port)
|
|
||||||
)
|
|
||||||
);
|
|
||||||
assert_eq!(
|
|
||||||
unsafe { &(*(::std::ptr::null::<sockaddr_in>())).sin_addr as *const _ as usize },
|
|
||||||
4usize,
|
|
||||||
concat!(
|
|
||||||
"Offset of field: ",
|
|
||||||
stringify!(sockaddr_in),
|
|
||||||
"::",
|
|
||||||
stringify!(sin_addr)
|
|
||||||
)
|
|
||||||
);
|
|
||||||
assert_eq!(
|
|
||||||
unsafe { &(*(::std::ptr::null::<sockaddr_in>())).sin_zero as *const _ as usize },
|
|
||||||
8usize,
|
|
||||||
concat!(
|
|
||||||
"Offset of field: ",
|
|
||||||
stringify!(sockaddr_in),
|
|
||||||
"::",
|
|
||||||
stringify!(sin_zero)
|
|
||||||
)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
#[repr(C)]
|
|
||||||
#[derive(Copy, Clone)]
|
|
||||||
pub struct sockaddr_in6 {
|
|
||||||
pub sin6_family: sa_family_t,
|
|
||||||
pub sin6_port: in_port_t,
|
|
||||||
pub sin6_flowinfo: u32,
|
|
||||||
pub sin6_addr: in6_addr,
|
|
||||||
pub sin6_scope_id: u32,
|
|
||||||
}
|
|
||||||
#[test]
|
|
||||||
fn bindgen_test_layout_sockaddr_in6() {
|
|
||||||
assert_eq!(
|
|
||||||
::std::mem::size_of::<sockaddr_in6>(),
|
|
||||||
28usize,
|
|
||||||
concat!("Size of: ", stringify!(sockaddr_in6))
|
|
||||||
);
|
|
||||||
assert_eq!(
|
|
||||||
::std::mem::align_of::<sockaddr_in6>(),
|
|
||||||
4usize,
|
|
||||||
concat!("Alignment of ", stringify!(sockaddr_in6))
|
|
||||||
);
|
|
||||||
assert_eq!(
|
|
||||||
unsafe { &(*(::std::ptr::null::<sockaddr_in6>())).sin6_family as *const _ as usize },
|
|
||||||
0usize,
|
|
||||||
concat!(
|
|
||||||
"Offset of field: ",
|
|
||||||
stringify!(sockaddr_in6),
|
|
||||||
"::",
|
|
||||||
stringify!(sin6_family)
|
|
||||||
)
|
|
||||||
);
|
|
||||||
assert_eq!(
|
|
||||||
unsafe { &(*(::std::ptr::null::<sockaddr_in6>())).sin6_port as *const _ as usize },
|
|
||||||
2usize,
|
|
||||||
concat!(
|
|
||||||
"Offset of field: ",
|
|
||||||
stringify!(sockaddr_in6),
|
|
||||||
"::",
|
|
||||||
stringify!(sin6_port)
|
|
||||||
)
|
|
||||||
);
|
|
||||||
assert_eq!(
|
|
||||||
unsafe { &(*(::std::ptr::null::<sockaddr_in6>())).sin6_flowinfo as *const _ as usize },
|
|
||||||
4usize,
|
|
||||||
concat!(
|
|
||||||
"Offset of field: ",
|
|
||||||
stringify!(sockaddr_in6),
|
|
||||||
"::",
|
|
||||||
stringify!(sin6_flowinfo)
|
|
||||||
)
|
|
||||||
);
|
|
||||||
assert_eq!(
|
|
||||||
unsafe { &(*(::std::ptr::null::<sockaddr_in6>())).sin6_addr as *const _ as usize },
|
|
||||||
8usize,
|
|
||||||
concat!(
|
|
||||||
"Offset of field: ",
|
|
||||||
stringify!(sockaddr_in6),
|
|
||||||
"::",
|
|
||||||
stringify!(sin6_addr)
|
|
||||||
)
|
|
||||||
);
|
|
||||||
assert_eq!(
|
|
||||||
unsafe { &(*(::std::ptr::null::<sockaddr_in6>())).sin6_scope_id as *const _ as usize },
|
|
||||||
24usize,
|
|
||||||
concat!(
|
|
||||||
"Offset of field: ",
|
|
||||||
stringify!(sockaddr_in6),
|
|
||||||
"::",
|
|
||||||
stringify!(sin6_scope_id)
|
|
||||||
)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
impl Default for sockaddr_in6 {
|
|
||||||
fn default() -> Self {
|
|
||||||
let mut s = ::std::mem::MaybeUninit::<Self>::uninit();
|
|
||||||
unsafe {
|
|
||||||
::std::ptr::write_bytes(s.as_mut_ptr(), 0, 1);
|
|
||||||
s.assume_init()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
impl ::std::fmt::Debug for sockaddr_in6 {
|
|
||||||
fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result {
|
|
||||||
write ! (f , "sockaddr_in6 {{ sin6_family: {:?}, sin6_port: {:?}, sin6_flowinfo: {:?}, sin6_addr: {:?}, sin6_scope_id: {:?} }}" , self . sin6_family , self . sin6_port , self . sin6_flowinfo , self . sin6_addr , self . sin6_scope_id)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
pub type wg_key = [u8; 32usize];
|
|
||||||
pub type wg_key_b64_string = [::std::os::raw::c_char; 45usize];
|
|
||||||
#[repr(C)]
|
|
||||||
#[derive(Debug, Default, Copy, Clone)]
|
|
||||||
pub struct timespec64 {
|
|
||||||
pub tv_sec: i64,
|
|
||||||
pub tv_nsec: i64,
|
|
||||||
}
|
|
||||||
#[test]
|
|
||||||
fn bindgen_test_layout_timespec64() {
|
|
||||||
assert_eq!(
|
|
||||||
::std::mem::size_of::<timespec64>(),
|
|
||||||
16usize,
|
|
||||||
concat!("Size of: ", stringify!(timespec64))
|
|
||||||
);
|
|
||||||
assert_eq!(
|
|
||||||
::std::mem::align_of::<timespec64>(),
|
|
||||||
8usize,
|
|
||||||
concat!("Alignment of ", stringify!(timespec64))
|
|
||||||
);
|
|
||||||
assert_eq!(
|
|
||||||
unsafe { &(*(::std::ptr::null::<timespec64>())).tv_sec as *const _ as usize },
|
|
||||||
0usize,
|
|
||||||
concat!(
|
|
||||||
"Offset of field: ",
|
|
||||||
stringify!(timespec64),
|
|
||||||
"::",
|
|
||||||
stringify!(tv_sec)
|
|
||||||
)
|
|
||||||
);
|
|
||||||
assert_eq!(
|
|
||||||
unsafe { &(*(::std::ptr::null::<timespec64>())).tv_nsec as *const _ as usize },
|
|
||||||
8usize,
|
|
||||||
concat!(
|
|
||||||
"Offset of field: ",
|
|
||||||
stringify!(timespec64),
|
|
||||||
"::",
|
|
||||||
stringify!(tv_nsec)
|
|
||||||
)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
#[repr(C)]
|
|
||||||
#[derive(Copy, Clone)]
|
|
||||||
pub struct wg_allowedip {
|
|
||||||
pub family: u16,
|
|
||||||
pub __bindgen_anon_1: wg_allowedip__bindgen_ty_1,
|
|
||||||
pub cidr: u8,
|
|
||||||
pub next_allowedip: *mut wg_allowedip,
|
|
||||||
}
|
|
||||||
#[repr(C)]
|
|
||||||
#[derive(Copy, Clone)]
|
|
||||||
pub union wg_allowedip__bindgen_ty_1 {
|
|
||||||
pub ip4: in_addr,
|
|
||||||
pub ip6: in6_addr,
|
|
||||||
}
|
|
||||||
#[test]
|
|
||||||
fn bindgen_test_layout_wg_allowedip__bindgen_ty_1() {
|
|
||||||
assert_eq!(
|
|
||||||
::std::mem::size_of::<wg_allowedip__bindgen_ty_1>(),
|
|
||||||
16usize,
|
|
||||||
concat!("Size of: ", stringify!(wg_allowedip__bindgen_ty_1))
|
|
||||||
);
|
|
||||||
assert_eq!(
|
|
||||||
::std::mem::align_of::<wg_allowedip__bindgen_ty_1>(),
|
|
||||||
4usize,
|
|
||||||
concat!("Alignment of ", stringify!(wg_allowedip__bindgen_ty_1))
|
|
||||||
);
|
|
||||||
assert_eq!(
|
|
||||||
unsafe { &(*(::std::ptr::null::<wg_allowedip__bindgen_ty_1>())).ip4 as *const _ as usize },
|
|
||||||
0usize,
|
|
||||||
concat!(
|
|
||||||
"Offset of field: ",
|
|
||||||
stringify!(wg_allowedip__bindgen_ty_1),
|
|
||||||
"::",
|
|
||||||
stringify!(ip4)
|
|
||||||
)
|
|
||||||
);
|
|
||||||
assert_eq!(
|
|
||||||
unsafe { &(*(::std::ptr::null::<wg_allowedip__bindgen_ty_1>())).ip6 as *const _ as usize },
|
|
||||||
0usize,
|
|
||||||
concat!(
|
|
||||||
"Offset of field: ",
|
|
||||||
stringify!(wg_allowedip__bindgen_ty_1),
|
|
||||||
"::",
|
|
||||||
stringify!(ip6)
|
|
||||||
)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
impl Default for wg_allowedip__bindgen_ty_1 {
|
|
||||||
fn default() -> Self {
|
|
||||||
let mut s = ::std::mem::MaybeUninit::<Self>::uninit();
|
|
||||||
unsafe {
|
|
||||||
::std::ptr::write_bytes(s.as_mut_ptr(), 0, 1);
|
|
||||||
s.assume_init()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
impl ::std::fmt::Debug for wg_allowedip__bindgen_ty_1 {
|
|
||||||
fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result {
|
|
||||||
write!(f, "wg_allowedip__bindgen_ty_1 {{ union }}")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
#[test]
|
|
||||||
fn bindgen_test_layout_wg_allowedip() {
|
|
||||||
assert_eq!(
|
|
||||||
::std::mem::size_of::<wg_allowedip>(),
|
|
||||||
32usize,
|
|
||||||
concat!("Size of: ", stringify!(wg_allowedip))
|
|
||||||
);
|
|
||||||
assert_eq!(
|
|
||||||
::std::mem::align_of::<wg_allowedip>(),
|
|
||||||
8usize,
|
|
||||||
concat!("Alignment of ", stringify!(wg_allowedip))
|
|
||||||
);
|
|
||||||
assert_eq!(
|
|
||||||
unsafe { &(*(::std::ptr::null::<wg_allowedip>())).family as *const _ as usize },
|
|
||||||
0usize,
|
|
||||||
concat!(
|
|
||||||
"Offset of field: ",
|
|
||||||
stringify!(wg_allowedip),
|
|
||||||
"::",
|
|
||||||
stringify!(family)
|
|
||||||
)
|
|
||||||
);
|
|
||||||
assert_eq!(
|
|
||||||
unsafe { &(*(::std::ptr::null::<wg_allowedip>())).cidr as *const _ as usize },
|
|
||||||
20usize,
|
|
||||||
concat!(
|
|
||||||
"Offset of field: ",
|
|
||||||
stringify!(wg_allowedip),
|
|
||||||
"::",
|
|
||||||
stringify!(cidr)
|
|
||||||
)
|
|
||||||
);
|
|
||||||
assert_eq!(
|
|
||||||
unsafe { &(*(::std::ptr::null::<wg_allowedip>())).next_allowedip as *const _ as usize },
|
|
||||||
24usize,
|
|
||||||
concat!(
|
|
||||||
"Offset of field: ",
|
|
||||||
stringify!(wg_allowedip),
|
|
||||||
"::",
|
|
||||||
stringify!(next_allowedip)
|
|
||||||
)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
impl Default for wg_allowedip {
|
|
||||||
fn default() -> Self {
|
|
||||||
let mut s = ::std::mem::MaybeUninit::<Self>::uninit();
|
|
||||||
unsafe {
|
|
||||||
::std::ptr::write_bytes(s.as_mut_ptr(), 0, 1);
|
|
||||||
s.assume_init()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
impl ::std::fmt::Debug for wg_allowedip {
|
|
||||||
fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result {
|
|
||||||
write ! (f , "wg_allowedip {{ family: {:?}, __bindgen_anon_1: {:?}, cidr: {:?}, next_allowedip: {:?} }}" , self . family , self . __bindgen_anon_1 , self . cidr , self . next_allowedip)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
impl wg_peer_flags {
|
|
||||||
pub const WGPEER_REMOVE_ME: wg_peer_flags = wg_peer_flags(1);
|
|
||||||
}
|
|
||||||
impl wg_peer_flags {
|
|
||||||
pub const WGPEER_REPLACE_ALLOWEDIPS: wg_peer_flags = wg_peer_flags(2);
|
|
||||||
}
|
|
||||||
impl wg_peer_flags {
|
|
||||||
pub const WGPEER_HAS_PUBLIC_KEY: wg_peer_flags = wg_peer_flags(4);
|
|
||||||
}
|
|
||||||
impl wg_peer_flags {
|
|
||||||
pub const WGPEER_HAS_PRESHARED_KEY: wg_peer_flags = wg_peer_flags(8);
|
|
||||||
}
|
|
||||||
impl wg_peer_flags {
|
|
||||||
pub const WGPEER_HAS_PERSISTENT_KEEPALIVE_INTERVAL: wg_peer_flags = wg_peer_flags(16);
|
|
||||||
}
|
|
||||||
impl ::std::ops::BitOr<wg_peer_flags> for wg_peer_flags {
|
|
||||||
type Output = Self;
|
|
||||||
#[inline]
|
|
||||||
fn bitor(self, other: Self) -> Self {
|
|
||||||
wg_peer_flags(self.0 | other.0)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
impl ::std::ops::BitOrAssign for wg_peer_flags {
|
|
||||||
#[inline]
|
|
||||||
fn bitor_assign(&mut self, rhs: wg_peer_flags) {
|
|
||||||
self.0 |= rhs.0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
impl ::std::ops::BitAnd<wg_peer_flags> for wg_peer_flags {
|
|
||||||
type Output = Self;
|
|
||||||
#[inline]
|
|
||||||
fn bitand(self, other: Self) -> Self {
|
|
||||||
wg_peer_flags(self.0 & other.0)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
impl ::std::ops::BitAndAssign for wg_peer_flags {
|
|
||||||
#[inline]
|
|
||||||
fn bitand_assign(&mut self, rhs: wg_peer_flags) {
|
|
||||||
self.0 &= rhs.0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
#[repr(transparent)]
|
|
||||||
#[derive(Debug, Copy, Clone, Hash, PartialEq, Eq)]
|
|
||||||
pub struct wg_peer_flags(pub ::std::os::raw::c_uint);
|
|
||||||
#[repr(C)]
|
|
||||||
#[derive(Copy, Clone)]
|
|
||||||
pub union wg_endpoint {
|
|
||||||
pub addr: sockaddr,
|
|
||||||
pub addr4: sockaddr_in,
|
|
||||||
pub addr6: sockaddr_in6,
|
|
||||||
}
|
|
||||||
#[test]
|
|
||||||
fn bindgen_test_layout_wg_endpoint() {
|
|
||||||
assert_eq!(
|
|
||||||
::std::mem::size_of::<wg_endpoint>(),
|
|
||||||
28usize,
|
|
||||||
concat!("Size of: ", stringify!(wg_endpoint))
|
|
||||||
);
|
|
||||||
assert_eq!(
|
|
||||||
::std::mem::align_of::<wg_endpoint>(),
|
|
||||||
4usize,
|
|
||||||
concat!("Alignment of ", stringify!(wg_endpoint))
|
|
||||||
);
|
|
||||||
assert_eq!(
|
|
||||||
unsafe { &(*(::std::ptr::null::<wg_endpoint>())).addr as *const _ as usize },
|
|
||||||
0usize,
|
|
||||||
concat!(
|
|
||||||
"Offset of field: ",
|
|
||||||
stringify!(wg_endpoint),
|
|
||||||
"::",
|
|
||||||
stringify!(addr)
|
|
||||||
)
|
|
||||||
);
|
|
||||||
assert_eq!(
|
|
||||||
unsafe { &(*(::std::ptr::null::<wg_endpoint>())).addr4 as *const _ as usize },
|
|
||||||
0usize,
|
|
||||||
concat!(
|
|
||||||
"Offset of field: ",
|
|
||||||
stringify!(wg_endpoint),
|
|
||||||
"::",
|
|
||||||
stringify!(addr4)
|
|
||||||
)
|
|
||||||
);
|
|
||||||
assert_eq!(
|
|
||||||
unsafe { &(*(::std::ptr::null::<wg_endpoint>())).addr6 as *const _ as usize },
|
|
||||||
0usize,
|
|
||||||
concat!(
|
|
||||||
"Offset of field: ",
|
|
||||||
stringify!(wg_endpoint),
|
|
||||||
"::",
|
|
||||||
stringify!(addr6)
|
|
||||||
)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
impl Default for wg_endpoint {
|
|
||||||
fn default() -> Self {
|
|
||||||
let mut s = ::std::mem::MaybeUninit::<Self>::uninit();
|
|
||||||
unsafe {
|
|
||||||
::std::ptr::write_bytes(s.as_mut_ptr(), 0, 1);
|
|
||||||
s.assume_init()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
impl ::std::fmt::Debug for wg_endpoint {
|
|
||||||
fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result {
|
|
||||||
write!(f, "wg_endpoint {{ union }}")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
#[repr(C)]
|
|
||||||
#[derive(Copy, Clone)]
|
|
||||||
pub struct wg_peer {
|
|
||||||
pub flags: wg_peer_flags,
|
|
||||||
pub public_key: wg_key,
|
|
||||||
pub preshared_key: wg_key,
|
|
||||||
pub endpoint: wg_endpoint,
|
|
||||||
pub last_handshake_time: timespec64,
|
|
||||||
pub rx_bytes: u64,
|
|
||||||
pub tx_bytes: u64,
|
|
||||||
pub persistent_keepalive_interval: u16,
|
|
||||||
pub first_allowedip: *mut wg_allowedip,
|
|
||||||
pub last_allowedip: *mut wg_allowedip,
|
|
||||||
pub next_peer: *mut wg_peer,
|
|
||||||
}
|
|
||||||
#[test]
|
|
||||||
fn bindgen_test_layout_wg_peer() {
|
|
||||||
assert_eq!(
|
|
||||||
::std::mem::size_of::<wg_peer>(),
|
|
||||||
160usize,
|
|
||||||
concat!("Size of: ", stringify!(wg_peer))
|
|
||||||
);
|
|
||||||
assert_eq!(
|
|
||||||
::std::mem::align_of::<wg_peer>(),
|
|
||||||
8usize,
|
|
||||||
concat!("Alignment of ", stringify!(wg_peer))
|
|
||||||
);
|
|
||||||
assert_eq!(
|
|
||||||
unsafe { &(*(::std::ptr::null::<wg_peer>())).flags as *const _ as usize },
|
|
||||||
0usize,
|
|
||||||
concat!(
|
|
||||||
"Offset of field: ",
|
|
||||||
stringify!(wg_peer),
|
|
||||||
"::",
|
|
||||||
stringify!(flags)
|
|
||||||
)
|
|
||||||
);
|
|
||||||
assert_eq!(
|
|
||||||
unsafe { &(*(::std::ptr::null::<wg_peer>())).public_key as *const _ as usize },
|
|
||||||
4usize,
|
|
||||||
concat!(
|
|
||||||
"Offset of field: ",
|
|
||||||
stringify!(wg_peer),
|
|
||||||
"::",
|
|
||||||
stringify!(public_key)
|
|
||||||
)
|
|
||||||
);
|
|
||||||
assert_eq!(
|
|
||||||
unsafe { &(*(::std::ptr::null::<wg_peer>())).preshared_key as *const _ as usize },
|
|
||||||
36usize,
|
|
||||||
concat!(
|
|
||||||
"Offset of field: ",
|
|
||||||
stringify!(wg_peer),
|
|
||||||
"::",
|
|
||||||
stringify!(preshared_key)
|
|
||||||
)
|
|
||||||
);
|
|
||||||
assert_eq!(
|
|
||||||
unsafe { &(*(::std::ptr::null::<wg_peer>())).endpoint as *const _ as usize },
|
|
||||||
68usize,
|
|
||||||
concat!(
|
|
||||||
"Offset of field: ",
|
|
||||||
stringify!(wg_peer),
|
|
||||||
"::",
|
|
||||||
stringify!(endpoint)
|
|
||||||
)
|
|
||||||
);
|
|
||||||
assert_eq!(
|
|
||||||
unsafe { &(*(::std::ptr::null::<wg_peer>())).last_handshake_time as *const _ as usize },
|
|
||||||
96usize,
|
|
||||||
concat!(
|
|
||||||
"Offset of field: ",
|
|
||||||
stringify!(wg_peer),
|
|
||||||
"::",
|
|
||||||
stringify!(last_handshake_time)
|
|
||||||
)
|
|
||||||
);
|
|
||||||
assert_eq!(
|
|
||||||
unsafe { &(*(::std::ptr::null::<wg_peer>())).rx_bytes as *const _ as usize },
|
|
||||||
112usize,
|
|
||||||
concat!(
|
|
||||||
"Offset of field: ",
|
|
||||||
stringify!(wg_peer),
|
|
||||||
"::",
|
|
||||||
stringify!(rx_bytes)
|
|
||||||
)
|
|
||||||
);
|
|
||||||
assert_eq!(
|
|
||||||
unsafe { &(*(::std::ptr::null::<wg_peer>())).tx_bytes as *const _ as usize },
|
|
||||||
120usize,
|
|
||||||
concat!(
|
|
||||||
"Offset of field: ",
|
|
||||||
stringify!(wg_peer),
|
|
||||||
"::",
|
|
||||||
stringify!(tx_bytes)
|
|
||||||
)
|
|
||||||
);
|
|
||||||
assert_eq!(
|
|
||||||
unsafe {
|
|
||||||
&(*(::std::ptr::null::<wg_peer>())).persistent_keepalive_interval as *const _ as usize
|
|
||||||
},
|
|
||||||
128usize,
|
|
||||||
concat!(
|
|
||||||
"Offset of field: ",
|
|
||||||
stringify!(wg_peer),
|
|
||||||
"::",
|
|
||||||
stringify!(persistent_keepalive_interval)
|
|
||||||
)
|
|
||||||
);
|
|
||||||
assert_eq!(
|
|
||||||
unsafe { &(*(::std::ptr::null::<wg_peer>())).first_allowedip as *const _ as usize },
|
|
||||||
136usize,
|
|
||||||
concat!(
|
|
||||||
"Offset of field: ",
|
|
||||||
stringify!(wg_peer),
|
|
||||||
"::",
|
|
||||||
stringify!(first_allowedip)
|
|
||||||
)
|
|
||||||
);
|
|
||||||
assert_eq!(
|
|
||||||
unsafe { &(*(::std::ptr::null::<wg_peer>())).last_allowedip as *const _ as usize },
|
|
||||||
144usize,
|
|
||||||
concat!(
|
|
||||||
"Offset of field: ",
|
|
||||||
stringify!(wg_peer),
|
|
||||||
"::",
|
|
||||||
stringify!(last_allowedip)
|
|
||||||
)
|
|
||||||
);
|
|
||||||
assert_eq!(
|
|
||||||
unsafe { &(*(::std::ptr::null::<wg_peer>())).next_peer as *const _ as usize },
|
|
||||||
152usize,
|
|
||||||
concat!(
|
|
||||||
"Offset of field: ",
|
|
||||||
stringify!(wg_peer),
|
|
||||||
"::",
|
|
||||||
stringify!(next_peer)
|
|
||||||
)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
impl Default for wg_peer {
|
|
||||||
fn default() -> Self {
|
|
||||||
let mut s = ::std::mem::MaybeUninit::<Self>::uninit();
|
|
||||||
unsafe {
|
|
||||||
::std::ptr::write_bytes(s.as_mut_ptr(), 0, 1);
|
|
||||||
s.assume_init()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
impl ::std::fmt::Debug for wg_peer {
|
|
||||||
fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result {
|
|
||||||
write ! (f , "wg_peer {{ flags: {:?}, public_key: [{}], preshared_key: [{}], endpoint: {:?}, last_handshake_time: {:?}, rx_bytes: {:?}, tx_bytes: {:?}, persistent_keepalive_interval: {:?}, first_allowedip: {:?}, last_allowedip: {:?}, next_peer: {:?} }}" , self . flags , self . public_key . iter () . enumerate () . map (| (i , v) | format ! ("{}{:?}" , if i > 0 { ", " } else { "" } , v)) . collect :: < String > () , self . preshared_key . iter () . enumerate () . map (| (i , v) | format ! ("{}{:?}" , if i > 0 { ", " } else { "" } , v)) . collect :: < String > () , self . endpoint , self . last_handshake_time , self . rx_bytes , self . tx_bytes , self . persistent_keepalive_interval , self . first_allowedip , self . last_allowedip , self . next_peer)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
impl wg_device_flags {
|
|
||||||
pub const WGDEVICE_REPLACE_PEERS: wg_device_flags = wg_device_flags(1);
|
|
||||||
}
|
|
||||||
impl wg_device_flags {
|
|
||||||
pub const WGDEVICE_HAS_PRIVATE_KEY: wg_device_flags = wg_device_flags(2);
|
|
||||||
}
|
|
||||||
impl wg_device_flags {
|
|
||||||
pub const WGDEVICE_HAS_PUBLIC_KEY: wg_device_flags = wg_device_flags(4);
|
|
||||||
}
|
|
||||||
impl wg_device_flags {
|
|
||||||
pub const WGDEVICE_HAS_LISTEN_PORT: wg_device_flags = wg_device_flags(8);
|
|
||||||
}
|
|
||||||
impl wg_device_flags {
|
|
||||||
pub const WGDEVICE_HAS_FWMARK: wg_device_flags = wg_device_flags(16);
|
|
||||||
}
|
|
||||||
impl ::std::ops::BitOr<wg_device_flags> for wg_device_flags {
|
|
||||||
type Output = Self;
|
|
||||||
#[inline]
|
|
||||||
fn bitor(self, other: Self) -> Self {
|
|
||||||
wg_device_flags(self.0 | other.0)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
impl ::std::ops::BitOrAssign for wg_device_flags {
|
|
||||||
#[inline]
|
|
||||||
fn bitor_assign(&mut self, rhs: wg_device_flags) {
|
|
||||||
self.0 |= rhs.0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
impl ::std::ops::BitAnd<wg_device_flags> for wg_device_flags {
|
|
||||||
type Output = Self;
|
|
||||||
#[inline]
|
|
||||||
fn bitand(self, other: Self) -> Self {
|
|
||||||
wg_device_flags(self.0 & other.0)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
impl ::std::ops::BitAndAssign for wg_device_flags {
|
|
||||||
#[inline]
|
|
||||||
fn bitand_assign(&mut self, rhs: wg_device_flags) {
|
|
||||||
self.0 &= rhs.0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
#[repr(transparent)]
|
|
||||||
#[derive(Debug, Copy, Clone, Hash, PartialEq, Eq)]
|
|
||||||
pub struct wg_device_flags(pub ::std::os::raw::c_uint);
|
|
||||||
#[repr(C)]
|
|
||||||
#[derive(Debug, Copy, Clone)]
|
|
||||||
pub struct wg_device {
|
|
||||||
pub name: [::std::os::raw::c_char; 16usize],
|
|
||||||
pub ifindex: u32,
|
|
||||||
pub flags: wg_device_flags,
|
|
||||||
pub public_key: wg_key,
|
|
||||||
pub private_key: wg_key,
|
|
||||||
pub fwmark: u32,
|
|
||||||
pub listen_port: u16,
|
|
||||||
pub first_peer: *mut wg_peer,
|
|
||||||
pub last_peer: *mut wg_peer,
|
|
||||||
}
|
|
||||||
#[test]
|
|
||||||
fn bindgen_test_layout_wg_device() {
|
|
||||||
assert_eq!(
|
|
||||||
::std::mem::size_of::<wg_device>(),
|
|
||||||
112usize,
|
|
||||||
concat!("Size of: ", stringify!(wg_device))
|
|
||||||
);
|
|
||||||
assert_eq!(
|
|
||||||
::std::mem::align_of::<wg_device>(),
|
|
||||||
8usize,
|
|
||||||
concat!("Alignment of ", stringify!(wg_device))
|
|
||||||
);
|
|
||||||
assert_eq!(
|
|
||||||
unsafe { &(*(::std::ptr::null::<wg_device>())).name as *const _ as usize },
|
|
||||||
0usize,
|
|
||||||
concat!(
|
|
||||||
"Offset of field: ",
|
|
||||||
stringify!(wg_device),
|
|
||||||
"::",
|
|
||||||
stringify!(name)
|
|
||||||
)
|
|
||||||
);
|
|
||||||
assert_eq!(
|
|
||||||
unsafe { &(*(::std::ptr::null::<wg_device>())).ifindex as *const _ as usize },
|
|
||||||
16usize,
|
|
||||||
concat!(
|
|
||||||
"Offset of field: ",
|
|
||||||
stringify!(wg_device),
|
|
||||||
"::",
|
|
||||||
stringify!(ifindex)
|
|
||||||
)
|
|
||||||
);
|
|
||||||
assert_eq!(
|
|
||||||
unsafe { &(*(::std::ptr::null::<wg_device>())).flags as *const _ as usize },
|
|
||||||
20usize,
|
|
||||||
concat!(
|
|
||||||
"Offset of field: ",
|
|
||||||
stringify!(wg_device),
|
|
||||||
"::",
|
|
||||||
stringify!(flags)
|
|
||||||
)
|
|
||||||
);
|
|
||||||
assert_eq!(
|
|
||||||
unsafe { &(*(::std::ptr::null::<wg_device>())).public_key as *const _ as usize },
|
|
||||||
24usize,
|
|
||||||
concat!(
|
|
||||||
"Offset of field: ",
|
|
||||||
stringify!(wg_device),
|
|
||||||
"::",
|
|
||||||
stringify!(public_key)
|
|
||||||
)
|
|
||||||
);
|
|
||||||
assert_eq!(
|
|
||||||
unsafe { &(*(::std::ptr::null::<wg_device>())).private_key as *const _ as usize },
|
|
||||||
56usize,
|
|
||||||
concat!(
|
|
||||||
"Offset of field: ",
|
|
||||||
stringify!(wg_device),
|
|
||||||
"::",
|
|
||||||
stringify!(private_key)
|
|
||||||
)
|
|
||||||
);
|
|
||||||
assert_eq!(
|
|
||||||
unsafe { &(*(::std::ptr::null::<wg_device>())).fwmark as *const _ as usize },
|
|
||||||
88usize,
|
|
||||||
concat!(
|
|
||||||
"Offset of field: ",
|
|
||||||
stringify!(wg_device),
|
|
||||||
"::",
|
|
||||||
stringify!(fwmark)
|
|
||||||
)
|
|
||||||
);
|
|
||||||
assert_eq!(
|
|
||||||
unsafe { &(*(::std::ptr::null::<wg_device>())).listen_port as *const _ as usize },
|
|
||||||
92usize,
|
|
||||||
concat!(
|
|
||||||
"Offset of field: ",
|
|
||||||
stringify!(wg_device),
|
|
||||||
"::",
|
|
||||||
stringify!(listen_port)
|
|
||||||
)
|
|
||||||
);
|
|
||||||
assert_eq!(
|
|
||||||
unsafe { &(*(::std::ptr::null::<wg_device>())).first_peer as *const _ as usize },
|
|
||||||
96usize,
|
|
||||||
concat!(
|
|
||||||
"Offset of field: ",
|
|
||||||
stringify!(wg_device),
|
|
||||||
"::",
|
|
||||||
stringify!(first_peer)
|
|
||||||
)
|
|
||||||
);
|
|
||||||
assert_eq!(
|
|
||||||
unsafe { &(*(::std::ptr::null::<wg_device>())).last_peer as *const _ as usize },
|
|
||||||
104usize,
|
|
||||||
concat!(
|
|
||||||
"Offset of field: ",
|
|
||||||
stringify!(wg_device),
|
|
||||||
"::",
|
|
||||||
stringify!(last_peer)
|
|
||||||
)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
impl Default for wg_device {
|
|
||||||
fn default() -> Self {
|
|
||||||
let mut s = ::std::mem::MaybeUninit::<Self>::uninit();
|
|
||||||
unsafe {
|
|
||||||
::std::ptr::write_bytes(s.as_mut_ptr(), 0, 1);
|
|
||||||
s.assume_init()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
extern "C" {
|
|
||||||
pub fn wg_set_device(dev: *mut wg_device) -> ::std::os::raw::c_int;
|
|
||||||
}
|
|
||||||
extern "C" {
|
|
||||||
pub fn wg_get_device(
|
|
||||||
dev: *mut *mut wg_device,
|
|
||||||
device_name: *const ::std::os::raw::c_char,
|
|
||||||
) -> ::std::os::raw::c_int;
|
|
||||||
}
|
|
||||||
extern "C" {
|
|
||||||
pub fn wg_add_device(device_name: *const ::std::os::raw::c_char) -> ::std::os::raw::c_int;
|
|
||||||
}
|
|
||||||
extern "C" {
|
|
||||||
pub fn wg_del_device(device_name: *const ::std::os::raw::c_char) -> ::std::os::raw::c_int;
|
|
||||||
}
|
|
||||||
extern "C" {
|
|
||||||
pub fn wg_free_device(dev: *mut wg_device);
|
|
||||||
}
|
|
||||||
extern "C" {
|
|
||||||
pub fn wg_list_device_names() -> *mut ::std::os::raw::c_char;
|
|
||||||
}
|
|
||||||
extern "C" {
|
|
||||||
pub fn wg_key_to_base64(base64: *mut ::std::os::raw::c_char, key: *mut u8);
|
|
||||||
}
|
|
||||||
extern "C" {
|
|
||||||
pub fn wg_key_from_base64(
|
|
||||||
key: *mut u8,
|
|
||||||
base64: *mut ::std::os::raw::c_char,
|
|
||||||
) -> ::std::os::raw::c_int;
|
|
||||||
}
|
|
||||||
extern "C" {
|
|
||||||
pub fn wg_key_is_zero(key: *mut u8) -> bool;
|
|
||||||
}
|
|
||||||
extern "C" {
|
|
||||||
pub fn wg_generate_public_key(public_key: *mut u8, private_key: *mut u8);
|
|
||||||
}
|
|
||||||
extern "C" {
|
|
||||||
pub fn wg_generate_private_key(private_key: *mut u8);
|
|
||||||
}
|
|
||||||
extern "C" {
|
|
||||||
pub fn wg_generate_preshared_key(preshared_key: *mut u8);
|
|
||||||
}
|
|
|
@ -1,49 +0,0 @@
|
||||||
#[cfg(target_os = "linux")]
|
|
||||||
mod linux {
|
|
||||||
use std::{env, path::PathBuf};
|
|
||||||
|
|
||||||
pub fn build_bindings() {
|
|
||||||
let out_path = PathBuf::from(env::var("OUT_DIR").unwrap());
|
|
||||||
|
|
||||||
#[cfg(feature = "buildtime_bindgen")]
|
|
||||||
{
|
|
||||||
let bindings = bindgen::Builder::default()
|
|
||||||
.rust_target(bindgen::RustTarget::Stable_1_40)
|
|
||||||
.derive_default(true)
|
|
||||||
.header("c/wireguard.h")
|
|
||||||
.impl_debug(true)
|
|
||||||
.allowlist_function("wg_.*")
|
|
||||||
.bitfield_enum("wg_peer_flags")
|
|
||||||
.bitfield_enum("wg_device_flags");
|
|
||||||
|
|
||||||
let bindings = bindings.generate().expect("Unable to generate bindings");
|
|
||||||
bindings
|
|
||||||
.write_to_file(out_path.join("bindings.rs"))
|
|
||||||
.expect("Couldn't write bindings!");
|
|
||||||
}
|
|
||||||
#[cfg(not(feature = "buildtime_bindgen"))]
|
|
||||||
{
|
|
||||||
std::fs::copy("bindgen-bindings/bindings.rs", out_path.join("bindings.rs"))
|
|
||||||
.expect("Could not copy bindings to output directory");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn build_library() {
|
|
||||||
cc::Build::new()
|
|
||||||
.file("c/wireguard.c")
|
|
||||||
.warnings(true)
|
|
||||||
.extra_warnings(true)
|
|
||||||
.warnings_into_errors(true)
|
|
||||||
.flag_if_supported("-Wno-unused-parameter")
|
|
||||||
.compile("wireguard");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(target_os = "linux")]
|
|
||||||
fn main() {
|
|
||||||
linux::build_bindings();
|
|
||||||
linux::build_library();
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(not(target_os = "linux"))]
|
|
||||||
fn main() {}
|
|
File diff suppressed because it is too large
Load Diff
|
@ -1,105 +0,0 @@
|
||||||
/* SPDX-License-Identifier: LGPL-2.1+ */
|
|
||||||
/*
|
|
||||||
* Copyright (C) 2015-2020 Jason A. Donenfeld <Jason@zx2c4.com>. All Rights Reserved.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#ifndef WIREGUARD_H
|
|
||||||
#define WIREGUARD_H
|
|
||||||
|
|
||||||
#include <net/if.h>
|
|
||||||
#include <netinet/in.h>
|
|
||||||
#include <sys/socket.h>
|
|
||||||
#include <time.h>
|
|
||||||
#include <stdint.h>
|
|
||||||
#include <stdbool.h>
|
|
||||||
|
|
||||||
typedef uint8_t wg_key[32];
|
|
||||||
typedef char wg_key_b64_string[((sizeof(wg_key) + 2) / 3) * 4 + 1];
|
|
||||||
|
|
||||||
/* Cross platform __kernel_timespec */
|
|
||||||
struct timespec64 {
|
|
||||||
int64_t tv_sec;
|
|
||||||
int64_t tv_nsec;
|
|
||||||
};
|
|
||||||
|
|
||||||
typedef struct wg_allowedip {
|
|
||||||
uint16_t family;
|
|
||||||
union {
|
|
||||||
struct in_addr ip4;
|
|
||||||
struct in6_addr ip6;
|
|
||||||
};
|
|
||||||
uint8_t cidr;
|
|
||||||
struct wg_allowedip *next_allowedip;
|
|
||||||
} wg_allowedip;
|
|
||||||
|
|
||||||
enum wg_peer_flags {
|
|
||||||
WGPEER_REMOVE_ME = 1U << 0,
|
|
||||||
WGPEER_REPLACE_ALLOWEDIPS = 1U << 1,
|
|
||||||
WGPEER_HAS_PUBLIC_KEY = 1U << 2,
|
|
||||||
WGPEER_HAS_PRESHARED_KEY = 1U << 3,
|
|
||||||
WGPEER_HAS_PERSISTENT_KEEPALIVE_INTERVAL = 1U << 4
|
|
||||||
};
|
|
||||||
|
|
||||||
typedef union wg_endpoint {
|
|
||||||
struct sockaddr addr;
|
|
||||||
struct sockaddr_in addr4;
|
|
||||||
struct sockaddr_in6 addr6;
|
|
||||||
} wg_endpoint;
|
|
||||||
|
|
||||||
typedef struct wg_peer {
|
|
||||||
enum wg_peer_flags flags;
|
|
||||||
|
|
||||||
wg_key public_key;
|
|
||||||
wg_key preshared_key;
|
|
||||||
|
|
||||||
wg_endpoint endpoint;
|
|
||||||
|
|
||||||
struct timespec64 last_handshake_time;
|
|
||||||
uint64_t rx_bytes, tx_bytes;
|
|
||||||
uint16_t persistent_keepalive_interval;
|
|
||||||
|
|
||||||
struct wg_allowedip *first_allowedip, *last_allowedip;
|
|
||||||
struct wg_peer *next_peer;
|
|
||||||
} wg_peer;
|
|
||||||
|
|
||||||
enum wg_device_flags {
|
|
||||||
WGDEVICE_REPLACE_PEERS = 1U << 0,
|
|
||||||
WGDEVICE_HAS_PRIVATE_KEY = 1U << 1,
|
|
||||||
WGDEVICE_HAS_PUBLIC_KEY = 1U << 2,
|
|
||||||
WGDEVICE_HAS_LISTEN_PORT = 1U << 3,
|
|
||||||
WGDEVICE_HAS_FWMARK = 1U << 4
|
|
||||||
};
|
|
||||||
|
|
||||||
typedef struct wg_device {
|
|
||||||
char name[IFNAMSIZ];
|
|
||||||
uint32_t ifindex;
|
|
||||||
|
|
||||||
enum wg_device_flags flags;
|
|
||||||
|
|
||||||
wg_key public_key;
|
|
||||||
wg_key private_key;
|
|
||||||
|
|
||||||
uint32_t fwmark;
|
|
||||||
uint16_t listen_port;
|
|
||||||
|
|
||||||
struct wg_peer *first_peer, *last_peer;
|
|
||||||
} wg_device;
|
|
||||||
|
|
||||||
#define wg_for_each_device_name(__names, __name, __len) for ((__name) = (__names), (__len) = 0; ((__len) = strlen(__name)); (__name) += (__len) + 1)
|
|
||||||
#define wg_for_each_peer(__dev, __peer) for ((__peer) = (__dev)->first_peer; (__peer); (__peer) = (__peer)->next_peer)
|
|
||||||
#define wg_for_each_allowedip(__peer, __allowedip) for ((__allowedip) = (__peer)->first_allowedip; (__allowedip); (__allowedip) = (__allowedip)->next_allowedip)
|
|
||||||
|
|
||||||
int wg_set_device(wg_device *dev);
|
|
||||||
int wg_get_device(wg_device **dev, const char *device_name);
|
|
||||||
int wg_add_device(const char *device_name);
|
|
||||||
int wg_del_device(const char *device_name);
|
|
||||||
void wg_free_device(wg_device *dev);
|
|
||||||
char *wg_list_device_names(void); /* first\0second\0third\0forth\0last\0\0 */
|
|
||||||
void wg_key_to_base64(wg_key_b64_string base64, const wg_key key);
|
|
||||||
int wg_key_from_base64(wg_key key, const wg_key_b64_string base64);
|
|
||||||
bool wg_key_is_zero(const wg_key key);
|
|
||||||
void wg_generate_public_key(wg_key public_key, const wg_key private_key);
|
|
||||||
void wg_generate_private_key(wg_key private_key);
|
|
||||||
void wg_generate_preshared_key(wg_key preshared_key);
|
|
||||||
|
|
||||||
#endif
|
|
|
@ -1,8 +0,0 @@
|
||||||
#![allow(non_upper_case_globals)]
|
|
||||||
#![allow(non_camel_case_types)]
|
|
||||||
#![allow(non_snake_case)]
|
|
||||||
// https://github.com/rust-lang/rust-bindgen/issues/1651
|
|
||||||
#![allow(deref_nullptr)]
|
|
||||||
|
|
||||||
#[cfg(target_os = "linux")]
|
|
||||||
include!(concat!(env!("OUT_DIR"), "/bindings.rs"));
|
|
|
@ -1,26 +0,0 @@
|
||||||
#!/bin/bash -e
|
|
||||||
# This script modified from https://github.com/rusqlite/rusqlite/blob/master/libsqlite3-sys/upgrade.sh
|
|
||||||
|
|
||||||
SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null 2>&1 && pwd )"
|
|
||||||
CUR_DIR=$(pwd -P)
|
|
||||||
echo "$SCRIPT_DIR"
|
|
||||||
cd "$SCRIPT_DIR" || { echo "fatal error" >&2; exit 1; }
|
|
||||||
cargo clean
|
|
||||||
mkdir -p "$SCRIPT_DIR/../target"
|
|
||||||
|
|
||||||
pushd "$SCRIPT_DIR/c"
|
|
||||||
curl -O https://raw.githubusercontent.com/WireGuard/wireguard-tools/master/contrib/embeddable-wg-library/wireguard.c
|
|
||||||
curl -O https://raw.githubusercontent.com/WireGuard/wireguard-tools/master/contrib/embeddable-wg-library/wireguard.h
|
|
||||||
popd
|
|
||||||
|
|
||||||
# Regenerate bindgen file
|
|
||||||
rm -f "bindgen-bindings/bindings.rs"
|
|
||||||
# Just to make sure there is only one bindgen.rs file in target dir
|
|
||||||
find "$SCRIPT_DIR/../target" -type f -name bindings.rs -exec rm {} \;
|
|
||||||
cargo build --features "buildtime_bindgen"
|
|
||||||
find "$SCRIPT_DIR/../target" -type f -name bindings.rs -exec mv {} "$SCRIPT_DIR/bindgen-bindings/bindings.rs" \;
|
|
||||||
|
|
||||||
# Sanity checks
|
|
||||||
cd "$SCRIPT_DIR" || { echo "fatal error" >&2; exit 1; }
|
|
||||||
cargo test
|
|
||||||
echo 'You should increment the version in Cargo.toml'
|
|
|
@ -13,10 +13,13 @@ version = "1.5.2"
|
||||||
base64 = "0.13"
|
base64 = "0.13"
|
||||||
hex = "0.4"
|
hex = "0.4"
|
||||||
libc = "0.2"
|
libc = "0.2"
|
||||||
|
|
||||||
[target.'cfg(target_os = "linux")'.dependencies]
|
|
||||||
wireguard-control-sys = { path = "../wireguard-control-sys", version = "1.5.2" }
|
|
||||||
|
|
||||||
[target.'cfg(not(target_os = "linux"))'.dependencies]
|
|
||||||
rand_core = "0.6"
|
rand_core = "0.6"
|
||||||
curve25519-dalek = "4.0.0-pre.1"
|
curve25519-dalek = "4.0.0-pre.1"
|
||||||
|
|
||||||
|
[target.'cfg(target_os = "linux")'.dependencies]
|
||||||
|
netlink-request = { path = "../netlink-request" }
|
||||||
|
netlink-sys = "0.8"
|
||||||
|
netlink-packet-core = "0.4"
|
||||||
|
netlink-packet-generic = "0.3"
|
||||||
|
netlink-packet-route = "0.10"
|
||||||
|
netlink-packet-wireguard = { git = "https://github.com/mcginty/netlink", branch = "wireguard-fixes" }
|
||||||
|
|
|
@ -0,0 +1,11 @@
|
||||||
|
use wireguard_control::{Backend, Device};
|
||||||
|
|
||||||
|
#[cfg(target_os = "linux")]
|
||||||
|
const BACKEND: Backend = Backend::Kernel;
|
||||||
|
#[cfg(not(target_os = "linux"))]
|
||||||
|
const BACKEND: Backend = Backend::Userspace;
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
let devices = Device::list(BACKEND).unwrap();
|
||||||
|
println!("{:?}", devices);
|
||||||
|
}
|
|
@ -1,536 +1,267 @@
|
||||||
use crate::{
|
use crate::{
|
||||||
device::AllowedIp, Backend, Device, DeviceUpdate, InterfaceName, InvalidInterfaceName,
|
device::AllowedIp, Backend, Device, DeviceUpdate, InterfaceName, Key, PeerConfig,
|
||||||
InvalidKey, PeerConfig, PeerConfigBuilder, PeerInfo, PeerStats,
|
PeerConfigBuilder, PeerInfo, PeerStats,
|
||||||
};
|
};
|
||||||
use wireguard_control_sys::{timespec64, wg_device_flags as wgdf, wg_peer_flags as wgpf};
|
use netlink_packet_core::{
|
||||||
|
NetlinkMessage, NetlinkPayload, NLM_F_ACK, NLM_F_CREATE, NLM_F_EXCL, NLM_F_REQUEST,
|
||||||
use std::{
|
|
||||||
ffi::{CStr, CString},
|
|
||||||
io,
|
|
||||||
net::{IpAddr, SocketAddr},
|
|
||||||
os::raw::c_char,
|
|
||||||
ptr, str,
|
|
||||||
time::{Duration, SystemTime},
|
|
||||||
};
|
};
|
||||||
|
use netlink_packet_generic::GenlMessage;
|
||||||
impl<'a> From<&'a wireguard_control_sys::wg_allowedip> for AllowedIp {
|
use netlink_packet_route::{
|
||||||
fn from(raw: &wireguard_control_sys::wg_allowedip) -> AllowedIp {
|
constants::*,
|
||||||
let addr = match i32::from(raw.family) {
|
link::{
|
||||||
libc::AF_INET => IpAddr::V4(unsafe { raw.__bindgen_anon_1.ip4.s_addr }.to_be().into()),
|
self,
|
||||||
libc::AF_INET6 => {
|
nlas::{Info, InfoKind},
|
||||||
IpAddr::V6(unsafe { raw.__bindgen_anon_1.ip6.__in6_u.__u6_addr8 }.into())
|
|
||||||
},
|
},
|
||||||
_ => unreachable!(format!("Unsupported socket family {}!", raw.family)),
|
LinkMessage, RtnlMessage,
|
||||||
};
|
};
|
||||||
|
use netlink_packet_wireguard::{
|
||||||
|
self,
|
||||||
|
constants::{WGDEVICE_F_REPLACE_PEERS, WGPEER_F_REMOVE_ME, WGPEER_F_REPLACE_ALLOWEDIPS},
|
||||||
|
nlas::{WgAllowedIpAttrs, WgDeviceAttrs, WgPeerAttrs},
|
||||||
|
Wireguard, WireguardCmd,
|
||||||
|
};
|
||||||
|
use netlink_request::{netlink_request_genl, netlink_request_rtnl};
|
||||||
|
|
||||||
AllowedIp {
|
use std::{convert::TryFrom, io};
|
||||||
address: addr,
|
|
||||||
cidr: raw.cidr,
|
macro_rules! get_nla_value {
|
||||||
|
($nlas:expr, $e:ident, $v:ident) => {
|
||||||
|
$nlas.iter().find_map(|attr| match attr {
|
||||||
|
$e::$v(value) => Some(value),
|
||||||
|
_ => None,
|
||||||
|
})
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl<'a> TryFrom<Vec<WgAllowedIpAttrs>> for AllowedIp {
|
||||||
|
type Error = io::Error;
|
||||||
|
|
||||||
|
fn try_from(attrs: Vec<WgAllowedIpAttrs>) -> Result<Self, Self::Error> {
|
||||||
|
let address = *get_nla_value!(attrs, WgAllowedIpAttrs, IpAddr)
|
||||||
|
.ok_or_else(|| io::ErrorKind::NotFound)?;
|
||||||
|
let cidr = *get_nla_value!(attrs, WgAllowedIpAttrs, Cidr)
|
||||||
|
.ok_or_else(|| io::ErrorKind::NotFound)?;
|
||||||
|
Ok(AllowedIp { address, cidr })
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> From<&'a wireguard_control_sys::wg_peer> for PeerInfo {
|
impl AllowedIp {
|
||||||
fn from(raw: &wireguard_control_sys::wg_peer) -> PeerInfo {
|
fn to_attrs(&self) -> Vec<WgAllowedIpAttrs> {
|
||||||
PeerInfo {
|
vec![
|
||||||
config: PeerConfig {
|
WgAllowedIpAttrs::Family(if self.address.is_ipv4() {
|
||||||
public_key: Key::from_raw(raw.public_key),
|
AF_INET
|
||||||
preshared_key: if (raw.flags & wgpf::WGPEER_HAS_PRESHARED_KEY).0 > 0 {
|
|
||||||
Some(Key::from_raw(raw.preshared_key))
|
|
||||||
} else {
|
} else {
|
||||||
None
|
AF_INET6
|
||||||
},
|
}),
|
||||||
endpoint: parse_endpoint(&raw.endpoint),
|
WgAllowedIpAttrs::IpAddr(self.address),
|
||||||
persistent_keepalive_interval: match raw.persistent_keepalive_interval {
|
WgAllowedIpAttrs::Cidr(self.cidr),
|
||||||
0 => None,
|
]
|
||||||
x => Some(x),
|
}
|
||||||
},
|
}
|
||||||
allowed_ips: parse_allowed_ips(raw),
|
|
||||||
|
impl PeerConfigBuilder {
|
||||||
|
fn to_attrs(&self) -> Vec<WgPeerAttrs> {
|
||||||
|
let mut attrs = vec![WgPeerAttrs::PublicKey(self.public_key.0)];
|
||||||
|
let mut flags = 0u32;
|
||||||
|
if let Some(endpoint) = self.endpoint {
|
||||||
|
attrs.push(WgPeerAttrs::Endpoint(endpoint));
|
||||||
|
}
|
||||||
|
if let Some(ref key) = self.preshared_key {
|
||||||
|
attrs.push(WgPeerAttrs::PresharedKey(key.0));
|
||||||
|
}
|
||||||
|
if let Some(i) = self.persistent_keepalive_interval {
|
||||||
|
attrs.push(WgPeerAttrs::PersistentKeepalive(i));
|
||||||
|
}
|
||||||
|
let allowed_ips: Vec<_> = self.allowed_ips.iter().map(AllowedIp::to_attrs).collect();
|
||||||
|
attrs.push(WgPeerAttrs::AllowedIps(allowed_ips));
|
||||||
|
if self.remove_me {
|
||||||
|
flags |= WGPEER_F_REMOVE_ME;
|
||||||
|
}
|
||||||
|
if self.replace_allowed_ips {
|
||||||
|
flags |= WGPEER_F_REPLACE_ALLOWEDIPS;
|
||||||
|
}
|
||||||
|
if flags != 0 {
|
||||||
|
attrs.push(WgPeerAttrs::Flags(flags));
|
||||||
|
}
|
||||||
|
attrs
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> TryFrom<Vec<WgPeerAttrs>> for PeerInfo {
|
||||||
|
type Error = io::Error;
|
||||||
|
|
||||||
|
fn try_from(attrs: Vec<WgPeerAttrs>) -> Result<Self, Self::Error> {
|
||||||
|
let public_key = get_nla_value!(attrs, WgPeerAttrs, PublicKey)
|
||||||
|
.map(|key| Key(*key))
|
||||||
|
.ok_or(io::ErrorKind::NotFound)?;
|
||||||
|
let preshared_key = get_nla_value!(attrs, WgPeerAttrs, PresharedKey).map(|key| Key(*key));
|
||||||
|
let endpoint = get_nla_value!(attrs, WgPeerAttrs, Endpoint).cloned();
|
||||||
|
let persistent_keepalive_interval =
|
||||||
|
get_nla_value!(attrs, WgPeerAttrs, PersistentKeepalive).cloned();
|
||||||
|
let allowed_ips = get_nla_value!(attrs, WgPeerAttrs, AllowedIps)
|
||||||
|
.cloned()
|
||||||
|
.unwrap_or_default()
|
||||||
|
.into_iter()
|
||||||
|
.map(AllowedIp::try_from)
|
||||||
|
.collect::<Result<Vec<_>, _>>()?;
|
||||||
|
let last_handshake_time = get_nla_value!(attrs, WgPeerAttrs, LastHandshake).cloned();
|
||||||
|
let rx_bytes = get_nla_value!(attrs, WgPeerAttrs, RxBytes)
|
||||||
|
.cloned()
|
||||||
|
.unwrap_or_default();
|
||||||
|
let tx_bytes = get_nla_value!(attrs, WgPeerAttrs, TxBytes)
|
||||||
|
.cloned()
|
||||||
|
.unwrap_or_default();
|
||||||
|
Ok(PeerInfo {
|
||||||
|
config: PeerConfig {
|
||||||
|
public_key,
|
||||||
|
preshared_key,
|
||||||
|
endpoint,
|
||||||
|
persistent_keepalive_interval,
|
||||||
|
allowed_ips,
|
||||||
__cant_construct_me: (),
|
__cant_construct_me: (),
|
||||||
},
|
},
|
||||||
stats: PeerStats {
|
stats: PeerStats {
|
||||||
last_handshake_time: match (
|
last_handshake_time,
|
||||||
raw.last_handshake_time.tv_sec,
|
rx_bytes,
|
||||||
raw.last_handshake_time.tv_nsec,
|
tx_bytes,
|
||||||
) {
|
|
||||||
(0, 0) => None,
|
|
||||||
(s, ns) => Some(SystemTime::UNIX_EPOCH + Duration::new(s as u64, ns as u32)),
|
|
||||||
},
|
},
|
||||||
rx_bytes: raw.rx_bytes,
|
})
|
||||||
tx_bytes: raw.tx_bytes,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> From<&'a wireguard_control_sys::wg_device> for Device {
|
impl<'a> TryFrom<&'a Wireguard> for Device {
|
||||||
fn from(raw: &wireguard_control_sys::wg_device) -> Device {
|
type Error = io::Error;
|
||||||
// SAFETY: The name string buffer came directly from wgctrl so its NUL terminated.
|
|
||||||
let name = unsafe { InterfaceName::from_wg(raw.name) };
|
fn try_from(wg: &'a Wireguard) -> Result<Self, Self::Error> {
|
||||||
Device {
|
let name = get_nla_value!(wg.nlas, WgDeviceAttrs, IfName)
|
||||||
|
.ok_or_else(|| io::ErrorKind::NotFound)?
|
||||||
|
.parse()?;
|
||||||
|
let public_key = get_nla_value!(wg.nlas, WgDeviceAttrs, PublicKey).map(|key| Key(*key));
|
||||||
|
let private_key = get_nla_value!(wg.nlas, WgDeviceAttrs, PrivateKey).map(|key| Key(*key));
|
||||||
|
let listen_port = get_nla_value!(wg.nlas, WgDeviceAttrs, ListenPort).cloned();
|
||||||
|
let fwmark = get_nla_value!(wg.nlas, WgDeviceAttrs, Fwmark).cloned();
|
||||||
|
let peers = get_nla_value!(wg.nlas, WgDeviceAttrs, Peers)
|
||||||
|
.cloned()
|
||||||
|
.unwrap_or_default()
|
||||||
|
.into_iter()
|
||||||
|
.map(PeerInfo::try_from)
|
||||||
|
.collect::<Result<Vec<_>, _>>()?;
|
||||||
|
Ok(Device {
|
||||||
name,
|
name,
|
||||||
public_key: if (raw.flags & wgdf::WGDEVICE_HAS_PUBLIC_KEY).0 > 0 {
|
public_key,
|
||||||
Some(Key::from_raw(raw.public_key))
|
private_key,
|
||||||
} else {
|
listen_port,
|
||||||
None
|
fwmark,
|
||||||
},
|
peers,
|
||||||
private_key: if (raw.flags & wgdf::WGDEVICE_HAS_PRIVATE_KEY).0 > 0 {
|
|
||||||
Some(Key::from_raw(raw.private_key))
|
|
||||||
} else {
|
|
||||||
None
|
|
||||||
},
|
|
||||||
fwmark: match raw.fwmark {
|
|
||||||
0 => None,
|
|
||||||
x => Some(x),
|
|
||||||
},
|
|
||||||
listen_port: match raw.listen_port {
|
|
||||||
0 => None,
|
|
||||||
x => Some(x),
|
|
||||||
},
|
|
||||||
peers: parse_peers(raw),
|
|
||||||
linked_name: None,
|
linked_name: None,
|
||||||
backend: Backend::Kernel,
|
backend: Backend::Kernel,
|
||||||
__cant_construct_me: (),
|
__cant_construct_me: (),
|
||||||
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
fn parse_peers(dev: &wireguard_control_sys::wg_device) -> Vec<PeerInfo> {
|
|
||||||
let mut result = Vec::new();
|
|
||||||
|
|
||||||
let mut current_peer = dev.first_peer;
|
|
||||||
|
|
||||||
if current_peer.is_null() {
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
loop {
|
|
||||||
let peer = unsafe { &*current_peer };
|
|
||||||
|
|
||||||
result.push(PeerInfo::from(peer));
|
|
||||||
|
|
||||||
if current_peer == dev.last_peer {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
current_peer = peer.next_peer;
|
|
||||||
}
|
|
||||||
|
|
||||||
result
|
|
||||||
}
|
|
||||||
|
|
||||||
fn parse_allowed_ips(peer: &wireguard_control_sys::wg_peer) -> Vec<AllowedIp> {
|
|
||||||
let mut result = Vec::new();
|
|
||||||
|
|
||||||
let mut current_ip: *mut wireguard_control_sys::wg_allowedip = peer.first_allowedip;
|
|
||||||
|
|
||||||
if current_ip.is_null() {
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
loop {
|
|
||||||
let ip = unsafe { &*current_ip };
|
|
||||||
|
|
||||||
result.push(AllowedIp::from(ip));
|
|
||||||
|
|
||||||
if current_ip == peer.last_allowedip {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
current_ip = ip.next_allowedip;
|
|
||||||
}
|
|
||||||
|
|
||||||
result
|
|
||||||
}
|
|
||||||
|
|
||||||
fn parse_endpoint(endpoint: &wireguard_control_sys::wg_endpoint) -> Option<SocketAddr> {
|
|
||||||
let addr = unsafe { endpoint.addr };
|
|
||||||
match i32::from(addr.sa_family) {
|
|
||||||
libc::AF_INET => {
|
|
||||||
let addr4 = unsafe { endpoint.addr4 };
|
|
||||||
Some(SocketAddr::new(
|
|
||||||
IpAddr::V4(u32::from_be(addr4.sin_addr.s_addr).into()),
|
|
||||||
u16::from_be(addr4.sin_port),
|
|
||||||
))
|
|
||||||
},
|
|
||||||
libc::AF_INET6 => {
|
|
||||||
let addr6 = unsafe { endpoint.addr6 };
|
|
||||||
let bytes = unsafe { addr6.sin6_addr.__in6_u.__u6_addr8 };
|
|
||||||
Some(SocketAddr::new(
|
|
||||||
IpAddr::V6(bytes.into()),
|
|
||||||
u16::from_be(addr6.sin6_port),
|
|
||||||
))
|
|
||||||
},
|
|
||||||
0 => None,
|
|
||||||
_ => unreachable!(format!("Unsupported socket family: {}!", addr.sa_family)),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn encode_allowedips(
|
|
||||||
allowed_ips: &[AllowedIp],
|
|
||||||
) -> (
|
|
||||||
*mut wireguard_control_sys::wg_allowedip,
|
|
||||||
*mut wireguard_control_sys::wg_allowedip,
|
|
||||||
) {
|
|
||||||
if allowed_ips.is_empty() {
|
|
||||||
return (ptr::null_mut(), ptr::null_mut());
|
|
||||||
}
|
|
||||||
|
|
||||||
let mut first_ip = ptr::null_mut();
|
|
||||||
let mut last_ip: *mut wireguard_control_sys::wg_allowedip = ptr::null_mut();
|
|
||||||
|
|
||||||
for ip in allowed_ips {
|
|
||||||
let mut wg_allowedip = Box::new(wireguard_control_sys::wg_allowedip {
|
|
||||||
family: 0,
|
|
||||||
__bindgen_anon_1: Default::default(),
|
|
||||||
cidr: ip.cidr,
|
|
||||||
next_allowedip: first_ip,
|
|
||||||
});
|
|
||||||
|
|
||||||
match ip.address {
|
|
||||||
IpAddr::V4(a) => {
|
|
||||||
wg_allowedip.family = libc::AF_INET as u16;
|
|
||||||
wg_allowedip.__bindgen_anon_1.ip4.s_addr = u32::to_be(a.into());
|
|
||||||
},
|
|
||||||
IpAddr::V6(a) => {
|
|
||||||
wg_allowedip.family = libc::AF_INET6 as u16;
|
|
||||||
wg_allowedip.__bindgen_anon_1.ip6.__in6_u.__u6_addr8 = a.octets();
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
first_ip = Box::into_raw(wg_allowedip);
|
|
||||||
if last_ip.is_null() {
|
|
||||||
last_ip = first_ip;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
(first_ip, last_ip)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn encode_endpoint(endpoint: Option<SocketAddr>) -> wireguard_control_sys::wg_endpoint {
|
|
||||||
match endpoint {
|
|
||||||
Some(SocketAddr::V4(s)) => {
|
|
||||||
let mut peer = wireguard_control_sys::wg_endpoint::default();
|
|
||||||
peer.addr4 = wireguard_control_sys::sockaddr_in {
|
|
||||||
sin_family: libc::AF_INET as u16,
|
|
||||||
sin_addr: wireguard_control_sys::in_addr {
|
|
||||||
s_addr: u32::from_be((*s.ip()).into()),
|
|
||||||
},
|
|
||||||
sin_port: u16::to_be(s.port()),
|
|
||||||
sin_zero: [0; 8],
|
|
||||||
};
|
|
||||||
peer
|
|
||||||
},
|
|
||||||
Some(SocketAddr::V6(s)) => {
|
|
||||||
let mut peer = wireguard_control_sys::wg_endpoint::default();
|
|
||||||
let in6_addr = wireguard_control_sys::in6_addr__bindgen_ty_1 {
|
|
||||||
__u6_addr8: s.ip().octets(),
|
|
||||||
};
|
|
||||||
peer.addr6 = wireguard_control_sys::sockaddr_in6 {
|
|
||||||
sin6_family: libc::AF_INET6 as u16,
|
|
||||||
sin6_addr: wireguard_control_sys::in6_addr { __in6_u: in6_addr },
|
|
||||||
sin6_port: u16::to_be(s.port()),
|
|
||||||
sin6_flowinfo: 0,
|
|
||||||
sin6_scope_id: 0,
|
|
||||||
};
|
|
||||||
peer
|
|
||||||
},
|
|
||||||
None => wireguard_control_sys::wg_endpoint::default(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn encode_peers(
|
|
||||||
peers: &[PeerConfigBuilder],
|
|
||||||
) -> (
|
|
||||||
*mut wireguard_control_sys::wg_peer,
|
|
||||||
*mut wireguard_control_sys::wg_peer,
|
|
||||||
) {
|
|
||||||
let mut first_peer = ptr::null_mut();
|
|
||||||
let mut last_peer: *mut wireguard_control_sys::wg_peer = ptr::null_mut();
|
|
||||||
|
|
||||||
for peer in peers {
|
|
||||||
let (first_allowedip, last_allowedip) = encode_allowedips(&peer.allowed_ips);
|
|
||||||
|
|
||||||
let mut wg_peer = Box::new(wireguard_control_sys::wg_peer {
|
|
||||||
public_key: peer.public_key.0,
|
|
||||||
preshared_key: wireguard_control_sys::wg_key::default(),
|
|
||||||
endpoint: encode_endpoint(peer.endpoint),
|
|
||||||
last_handshake_time: timespec64 {
|
|
||||||
tv_sec: 0,
|
|
||||||
tv_nsec: 0,
|
|
||||||
},
|
|
||||||
tx_bytes: 0,
|
|
||||||
rx_bytes: 0,
|
|
||||||
persistent_keepalive_interval: 0,
|
|
||||||
first_allowedip,
|
|
||||||
last_allowedip,
|
|
||||||
next_peer: first_peer,
|
|
||||||
flags: wgpf::WGPEER_HAS_PUBLIC_KEY,
|
|
||||||
});
|
|
||||||
|
|
||||||
if let Some(Key(k)) = peer.preshared_key {
|
|
||||||
wg_peer.flags |= wgpf::WGPEER_HAS_PRESHARED_KEY;
|
|
||||||
wg_peer.preshared_key = k;
|
|
||||||
}
|
|
||||||
|
|
||||||
if let Some(n) = peer.persistent_keepalive_interval {
|
|
||||||
wg_peer.persistent_keepalive_interval = n;
|
|
||||||
wg_peer.flags |= wgpf::WGPEER_HAS_PERSISTENT_KEEPALIVE_INTERVAL;
|
|
||||||
}
|
|
||||||
|
|
||||||
if peer.replace_allowed_ips {
|
|
||||||
wg_peer.flags |= wgpf::WGPEER_REPLACE_ALLOWEDIPS;
|
|
||||||
}
|
|
||||||
|
|
||||||
if peer.remove_me {
|
|
||||||
wg_peer.flags |= wgpf::WGPEER_REMOVE_ME;
|
|
||||||
}
|
|
||||||
|
|
||||||
first_peer = Box::into_raw(wg_peer);
|
|
||||||
if last_peer.is_null() {
|
|
||||||
last_peer = first_peer;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
(first_peer, last_peer)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn enumerate() -> Result<Vec<InterfaceName>, io::Error> {
|
pub fn enumerate() -> Result<Vec<InterfaceName>, io::Error> {
|
||||||
let base = unsafe { wireguard_control_sys::wg_list_device_names() };
|
let link_responses = netlink_request_rtnl(
|
||||||
|
RtnlMessage::GetLink(LinkMessage::default()),
|
||||||
|
Some(NLM_F_DUMP | NLM_F_REQUEST),
|
||||||
|
)?;
|
||||||
|
let links = link_responses
|
||||||
|
.into_iter()
|
||||||
|
// Filter out non-link messages
|
||||||
|
.filter_map(|response| match response {
|
||||||
|
NetlinkMessage {
|
||||||
|
payload: NetlinkPayload::InnerMessage(RtnlMessage::NewLink(link)),
|
||||||
|
..
|
||||||
|
} => Some(link),
|
||||||
|
_ => None,
|
||||||
|
})
|
||||||
|
.filter(|link| {
|
||||||
|
for nla in link.nlas.iter() {
|
||||||
|
if let link::nlas::Nla::Info(infos) = nla {
|
||||||
|
return infos.iter().any(|info| info == &Info::Kind(InfoKind::Wireguard))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
false
|
||||||
|
})
|
||||||
|
.filter_map(|link| link.nlas.iter().find_map(|nla| match nla {
|
||||||
|
link::nlas::Nla::IfName(name) => Some(name.clone()),
|
||||||
|
_ => None,
|
||||||
|
}))
|
||||||
|
.filter_map(|name| name.parse().ok())
|
||||||
|
.collect::<Vec<_>>();
|
||||||
|
|
||||||
if base.is_null() {
|
Ok(links)
|
||||||
return Err(io::Error::last_os_error());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut current = base;
|
fn add_del(iface: &InterfaceName, add: bool) -> io::Result<()> {
|
||||||
let mut result = Vec::new();
|
let mut message = LinkMessage::default();
|
||||||
|
message
|
||||||
loop {
|
.nlas
|
||||||
let next_dev = unsafe { CStr::from_ptr(current).to_bytes() };
|
.push(link::nlas::Nla::IfName(iface.as_str_lossy().to_string()));
|
||||||
|
message.nlas.push(link::nlas::Nla::Info(vec![Info::Kind(
|
||||||
let len = next_dev.len();
|
link::nlas::InfoKind::Wireguard,
|
||||||
|
)]));
|
||||||
if len == 0 {
|
let extra_flags = if add { NLM_F_CREATE | NLM_F_EXCL } else { 0 };
|
||||||
break;
|
let rtnl_message = if add {
|
||||||
|
RtnlMessage::NewLink(message)
|
||||||
|
} else {
|
||||||
|
RtnlMessage::DelLink(message)
|
||||||
|
};
|
||||||
|
let result = netlink_request_rtnl(rtnl_message, Some(NLM_F_REQUEST | NLM_F_ACK | extra_flags));
|
||||||
|
match result {
|
||||||
|
Err(e) if e.kind() != io::ErrorKind::AlreadyExists => Err(e),
|
||||||
|
_ => Ok(()),
|
||||||
}
|
}
|
||||||
|
|
||||||
current = unsafe { current.add(len + 1) };
|
|
||||||
|
|
||||||
let interface: InterfaceName = str::from_utf8(next_dev)
|
|
||||||
.map_err(|_| InvalidInterfaceName::InvalidChars)?
|
|
||||||
.parse()?;
|
|
||||||
|
|
||||||
result.push(interface);
|
|
||||||
}
|
|
||||||
|
|
||||||
unsafe { libc::free(base as *mut libc::c_void) };
|
|
||||||
|
|
||||||
Ok(result)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn apply(builder: &DeviceUpdate, iface: &InterfaceName) -> io::Result<()> {
|
pub fn apply(builder: &DeviceUpdate, iface: &InterfaceName) -> io::Result<()> {
|
||||||
let (first_peer, last_peer) = encode_peers(&builder.peers);
|
add_del(iface, true)?;
|
||||||
|
let mut nlas = vec![WgDeviceAttrs::IfName(iface.as_str_lossy().to_string())];
|
||||||
let result = unsafe { wireguard_control_sys::wg_add_device(iface.as_ptr()) };
|
|
||||||
match result {
|
|
||||||
0 | -17 => {},
|
|
||||||
_ => return Err(io::Error::last_os_error()),
|
|
||||||
};
|
|
||||||
|
|
||||||
let mut wg_device = Box::new(wireguard_control_sys::wg_device {
|
|
||||||
name: iface.into_inner(),
|
|
||||||
ifindex: 0,
|
|
||||||
public_key: wireguard_control_sys::wg_key::default(),
|
|
||||||
private_key: wireguard_control_sys::wg_key::default(),
|
|
||||||
fwmark: 0,
|
|
||||||
listen_port: 0,
|
|
||||||
first_peer,
|
|
||||||
last_peer,
|
|
||||||
flags: wgdf(0),
|
|
||||||
});
|
|
||||||
|
|
||||||
if let Some(Key(k)) = builder.public_key {
|
|
||||||
wg_device.public_key = k;
|
|
||||||
wg_device.flags |= wgdf::WGDEVICE_HAS_PUBLIC_KEY;
|
|
||||||
}
|
|
||||||
|
|
||||||
if let Some(Key(k)) = builder.private_key {
|
if let Some(Key(k)) = builder.private_key {
|
||||||
wg_device.private_key = k;
|
nlas.push(WgDeviceAttrs::PrivateKey(k));
|
||||||
wg_device.flags |= wgdf::WGDEVICE_HAS_PRIVATE_KEY;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Some(f) = builder.fwmark {
|
if let Some(f) = builder.fwmark {
|
||||||
wg_device.fwmark = f;
|
nlas.push(WgDeviceAttrs::Fwmark(f));
|
||||||
wg_device.flags |= wgdf::WGDEVICE_HAS_FWMARK;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Some(f) = builder.listen_port {
|
if let Some(f) = builder.listen_port {
|
||||||
wg_device.listen_port = f;
|
nlas.push(WgDeviceAttrs::ListenPort(f));
|
||||||
wg_device.flags |= wgdf::WGDEVICE_HAS_LISTEN_PORT;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if builder.replace_peers {
|
if builder.replace_peers {
|
||||||
wg_device.flags |= wgdf::WGDEVICE_REPLACE_PEERS;
|
nlas.push(WgDeviceAttrs::Flags(WGDEVICE_F_REPLACE_PEERS));
|
||||||
}
|
}
|
||||||
|
let peers: Vec<Vec<_>> = builder
|
||||||
let ptr = Box::into_raw(wg_device);
|
.peers
|
||||||
let result = unsafe { wireguard_control_sys::wg_set_device(ptr) };
|
.iter()
|
||||||
|
.map(PeerConfigBuilder::to_attrs)
|
||||||
unsafe { wireguard_control_sys::wg_free_device(ptr) };
|
.collect();
|
||||||
|
nlas.push(WgDeviceAttrs::Peers(peers));
|
||||||
if result == 0 {
|
let genlmsg: GenlMessage<Wireguard> = GenlMessage::from_payload(Wireguard {
|
||||||
|
cmd: WireguardCmd::SetDevice,
|
||||||
|
nlas,
|
||||||
|
});
|
||||||
|
netlink_request_genl(genlmsg, Some(NLM_F_REQUEST | NLM_F_ACK))?;
|
||||||
Ok(())
|
Ok(())
|
||||||
} else {
|
|
||||||
Err(io::Error::last_os_error())
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_by_name(name: &InterfaceName) -> Result<Device, io::Error> {
|
pub fn get_by_name(name: &InterfaceName) -> Result<Device, io::Error> {
|
||||||
let mut device: *mut wireguard_control_sys::wg_device = ptr::null_mut();
|
let genlmsg: GenlMessage<Wireguard> = GenlMessage::from_payload(Wireguard {
|
||||||
|
cmd: WireguardCmd::GetDevice,
|
||||||
|
nlas: vec![WgDeviceAttrs::IfName(name.as_str_lossy().to_string())],
|
||||||
|
});
|
||||||
|
let responses = netlink_request_genl(genlmsg, Some(NLM_F_REQUEST | NLM_F_DUMP | NLM_F_ACK))?;
|
||||||
|
|
||||||
let result = unsafe {
|
match responses.get(0) {
|
||||||
wireguard_control_sys::wg_get_device(
|
Some(NetlinkMessage {
|
||||||
(&mut device) as *mut _ as *mut *mut wireguard_control_sys::wg_device,
|
payload: NetlinkPayload::InnerMessage(message),
|
||||||
name.as_ptr(),
|
..
|
||||||
)
|
}) => Device::try_from(&message.payload),
|
||||||
};
|
_ => Err(io::Error::new(
|
||||||
|
io::ErrorKind::InvalidData,
|
||||||
let result = if result == 0 {
|
"Unexpected netlink payload",
|
||||||
Ok(Device::from(unsafe { &*device }))
|
)),
|
||||||
} else {
|
}
|
||||||
Err(io::Error::last_os_error())
|
|
||||||
};
|
|
||||||
|
|
||||||
unsafe { wireguard_control_sys::wg_free_device(device) };
|
|
||||||
|
|
||||||
result
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn delete_interface(iface: &InterfaceName) -> io::Result<()> {
|
pub fn delete_interface(iface: &InterfaceName) -> io::Result<()> {
|
||||||
let result = unsafe { wireguard_control_sys::wg_del_device(iface.as_ptr()) };
|
add_del(iface, false)
|
||||||
|
|
||||||
if result == 0 {
|
|
||||||
Ok(())
|
|
||||||
} else {
|
|
||||||
Err(io::Error::last_os_error())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Represents a WireGuard encryption key.
|
|
||||||
///
|
|
||||||
/// WireGuard makes no meaningful distinction between public,
|
|
||||||
/// private and preshared keys - any sequence of 32 bytes
|
|
||||||
/// can be used as either of those.
|
|
||||||
///
|
|
||||||
/// This means that you need to be careful when working with
|
|
||||||
/// `Key`s, especially ones created from external data.
|
|
||||||
#[cfg(target_os = "linux")]
|
|
||||||
#[derive(PartialEq, Eq, Clone)]
|
|
||||||
pub struct Key(wireguard_control_sys::wg_key);
|
|
||||||
|
|
||||||
#[cfg(target_os = "linux")]
|
|
||||||
impl Key {
|
|
||||||
/// Creates a new `Key` from raw bytes.
|
|
||||||
pub fn from_raw(key: wireguard_control_sys::wg_key) -> Self {
|
|
||||||
Self(key)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Generates and returns a new private key.
|
|
||||||
pub fn generate_private() -> Self {
|
|
||||||
let mut private_key = wireguard_control_sys::wg_key::default();
|
|
||||||
|
|
||||||
unsafe {
|
|
||||||
wireguard_control_sys::wg_generate_private_key(private_key.as_mut_ptr());
|
|
||||||
}
|
|
||||||
|
|
||||||
Self(private_key)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Generates and returns a new preshared key.
|
|
||||||
pub fn generate_preshared() -> Self {
|
|
||||||
let mut preshared_key = wireguard_control_sys::wg_key::default();
|
|
||||||
|
|
||||||
unsafe {
|
|
||||||
wireguard_control_sys::wg_generate_preshared_key(preshared_key.as_mut_ptr());
|
|
||||||
}
|
|
||||||
|
|
||||||
Self(preshared_key)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Generates a public key for this private key.
|
|
||||||
pub fn generate_public(&self) -> Self {
|
|
||||||
let mut public_key = wireguard_control_sys::wg_key::default();
|
|
||||||
|
|
||||||
unsafe {
|
|
||||||
wireguard_control_sys::wg_generate_public_key(
|
|
||||||
public_key.as_mut_ptr(),
|
|
||||||
&self.0 as *const u8 as *mut u8,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
Self(public_key)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Generates an all-zero key.
|
|
||||||
pub fn zero() -> Self {
|
|
||||||
Self(wireguard_control_sys::wg_key::default())
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn as_bytes(&self) -> &[u8] {
|
|
||||||
&self.0
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Converts the key to a standardized base64 representation, as used by the `wg` utility and `wg-quick`.
|
|
||||||
pub fn to_base64(&self) -> String {
|
|
||||||
let mut key_b64: wireguard_control_sys::wg_key_b64_string = [0; 45];
|
|
||||||
unsafe {
|
|
||||||
wireguard_control_sys::wg_key_to_base64(
|
|
||||||
key_b64.as_mut_ptr(),
|
|
||||||
&self.0 as *const u8 as *mut u8,
|
|
||||||
);
|
|
||||||
|
|
||||||
str::from_utf8_unchecked(&*(&key_b64[..44] as *const [c_char] as *const [u8])).into()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Converts a base64 representation of the key to the raw bytes.
|
|
||||||
///
|
|
||||||
/// This can fail, as not all text input is valid base64 - in this case
|
|
||||||
/// `Err(InvalidKey)` is returned.
|
|
||||||
pub fn from_base64(key: &str) -> Result<Self, InvalidKey> {
|
|
||||||
let mut decoded = wireguard_control_sys::wg_key::default();
|
|
||||||
|
|
||||||
let key_str = CString::new(key)?;
|
|
||||||
let result = unsafe {
|
|
||||||
wireguard_control_sys::wg_key_from_base64(
|
|
||||||
decoded.as_mut_ptr(),
|
|
||||||
key_str.as_ptr() as *mut _,
|
|
||||||
)
|
|
||||||
};
|
|
||||||
|
|
||||||
if result == 0 {
|
|
||||||
Ok(Self { 0: decoded })
|
|
||||||
} else {
|
|
||||||
Err(InvalidKey)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn from_hex(hex_str: &str) -> Result<Self, InvalidKey> {
|
|
||||||
let bytes = hex::decode(hex_str).map_err(|_| InvalidKey)?;
|
|
||||||
Self::from_base64(&base64::encode(&bytes))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(test)]
|
|
||||||
mod tests {
|
|
||||||
use super::*;
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_encode_endpoint() -> Result<(), Box<dyn std::error::Error>> {
|
|
||||||
let endpoint = Some("1.2.3.4:51820".parse()?);
|
|
||||||
let endpoint6: Option<SocketAddr> = Some("[2001:db8:1::1]:51820".parse()?);
|
|
||||||
let encoded = encode_endpoint(endpoint);
|
|
||||||
let encoded6 = encode_endpoint(endpoint6);
|
|
||||||
assert_eq!(endpoint, parse_endpoint(&encoded));
|
|
||||||
assert_eq!(endpoint6, parse_endpoint(&encoded6));
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,5 @@
|
||||||
use crate::{Backend, Device, DeviceUpdate, InterfaceName, PeerConfig, PeerInfo, PeerStats};
|
use crate::{Backend, Device, DeviceUpdate, InterfaceName, PeerConfig, PeerInfo, PeerStats};
|
||||||
|
|
||||||
#[cfg(target_os = "linux")]
|
|
||||||
use crate::Key;
|
use crate::Key;
|
||||||
|
|
||||||
use std::{
|
use std::{
|
||||||
|
@ -382,131 +381,3 @@ pub fn apply(builder: &DeviceUpdate, iface: &InterfaceName) -> io::Result<()> {
|
||||||
_ => Err(io::ErrorKind::Other.into()),
|
_ => Err(io::ErrorKind::Other.into()),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Represents a WireGuard encryption key.
|
|
||||||
///
|
|
||||||
/// WireGuard makes no meaningful distinction between public,
|
|
||||||
/// private and preshared keys - any sequence of 32 bytes
|
|
||||||
/// can be used as either of those.
|
|
||||||
///
|
|
||||||
/// This means that you need to be careful when working with
|
|
||||||
/// `Key`s, especially ones created from external data.
|
|
||||||
#[cfg(not(target_os = "linux"))]
|
|
||||||
#[derive(PartialEq, Eq, Clone)]
|
|
||||||
pub struct Key([u8; 32]);
|
|
||||||
|
|
||||||
#[cfg(not(target_os = "linux"))]
|
|
||||||
impl Key {
|
|
||||||
/// Generates and returns a new private key.
|
|
||||||
pub fn generate_private() -> Self {
|
|
||||||
use rand_core::{OsRng, RngCore};
|
|
||||||
|
|
||||||
let mut bytes = [0u8; 32];
|
|
||||||
OsRng.fill_bytes(&mut bytes);
|
|
||||||
|
|
||||||
// Apply key clamping.
|
|
||||||
bytes[0] &= 248;
|
|
||||||
bytes[31] &= 127;
|
|
||||||
bytes[31] |= 64;
|
|
||||||
Self(bytes)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Generates and returns a new preshared key.
|
|
||||||
pub fn generate_preshared() -> Self {
|
|
||||||
use rand_core::{OsRng, RngCore};
|
|
||||||
|
|
||||||
let mut key = [0u8; 32];
|
|
||||||
OsRng.fill_bytes(&mut key);
|
|
||||||
Self(key)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Generates a public key for this private key.
|
|
||||||
pub fn generate_public(&self) -> Self {
|
|
||||||
use curve25519_dalek::scalar::Scalar;
|
|
||||||
|
|
||||||
use curve25519_dalek::constants::ED25519_BASEPOINT_TABLE;
|
|
||||||
|
|
||||||
// https://github.com/dalek-cryptography/x25519-dalek/blob/1c39ff92e0dfc0b24aa02d694f26f3b9539322a5/src/x25519.rs#L150
|
|
||||||
let point = (&ED25519_BASEPOINT_TABLE * &Scalar::from_bits(self.0)).to_montgomery();
|
|
||||||
|
|
||||||
Self(point.to_bytes())
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Generates an all-zero key.
|
|
||||||
pub fn zero() -> Self {
|
|
||||||
Self([0u8; 32])
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn as_bytes(&self) -> &[u8] {
|
|
||||||
&self.0
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Converts the key to a standardized base64 representation, as used by the `wg` utility and `wg-quick`.
|
|
||||||
pub fn to_base64(&self) -> String {
|
|
||||||
base64::encode(&self.0)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Converts a base64 representation of the key to the raw bytes.
|
|
||||||
///
|
|
||||||
/// This can fail, as not all text input is valid base64 - in this case
|
|
||||||
/// `Err(InvalidKey)` is returned.
|
|
||||||
pub fn from_base64(key: &str) -> Result<Self, crate::InvalidKey> {
|
|
||||||
use crate::InvalidKey;
|
|
||||||
|
|
||||||
let mut key_bytes = [0u8; 32];
|
|
||||||
let decoded_bytes = base64::decode(key).map_err(|_| InvalidKey)?;
|
|
||||||
|
|
||||||
if decoded_bytes.len() != 32 {
|
|
||||||
return Err(InvalidKey);
|
|
||||||
}
|
|
||||||
|
|
||||||
key_bytes.copy_from_slice(&decoded_bytes[..]);
|
|
||||||
Ok(Self(key_bytes))
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn from_hex(hex_str: &str) -> Result<Self, crate::InvalidKey> {
|
|
||||||
use crate::InvalidKey;
|
|
||||||
|
|
||||||
let mut sized_bytes = [0u8; 32];
|
|
||||||
hex::decode_to_slice(hex_str, &mut sized_bytes).map_err(|_| InvalidKey)?;
|
|
||||||
Ok(Self(sized_bytes))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(test)]
|
|
||||||
mod test {
|
|
||||||
use super::*;
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_pubkey_generation() {
|
|
||||||
let privkey = "SGb+ojrRNDuMePufwtIYhXzA//k6wF3R21tEBgKlzlM=";
|
|
||||||
let pubkey = "DD5yKRfzExcV5+kDnTroDgCU15latdMjiQ59j1hEuk8=";
|
|
||||||
|
|
||||||
let private = Key::from_base64(privkey).unwrap();
|
|
||||||
let public = Key::generate_public(&private);
|
|
||||||
|
|
||||||
assert_eq!(public.to_base64(), pubkey);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_rng_sanity_private() {
|
|
||||||
let first = Key::generate_private();
|
|
||||||
assert!(first.as_bytes() != [0u8; 32]);
|
|
||||||
for _ in 0..100_000 {
|
|
||||||
let key = Key::generate_private();
|
|
||||||
assert!(first != key);
|
|
||||||
assert!(key.as_bytes() != [0u8; 32]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_rng_sanity_preshared() {
|
|
||||||
let first = Key::generate_preshared();
|
|
||||||
assert!(first.as_bytes() != [0u8; 32]);
|
|
||||||
for _ in 0..100_000 {
|
|
||||||
let key = Key::generate_preshared();
|
|
||||||
assert!(first != key);
|
|
||||||
assert!(key.as_bytes() != [0u8; 32]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
|
@ -102,29 +102,34 @@ impl PeerConfigBuilder {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Specifies a preshared key to be set for this peer.
|
/// Specifies a preshared key to be set for this peer.
|
||||||
|
#[must_use]
|
||||||
pub fn set_preshared_key(mut self, key: Key) -> Self {
|
pub fn set_preshared_key(mut self, key: Key) -> Self {
|
||||||
self.preshared_key = Some(key);
|
self.preshared_key = Some(key);
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Specifies that this peer's preshared key should be unset.
|
/// Specifies that this peer's preshared key should be unset.
|
||||||
|
#[must_use]
|
||||||
pub fn unset_preshared_key(self) -> Self {
|
pub fn unset_preshared_key(self) -> Self {
|
||||||
self.set_preshared_key(Key::zero())
|
self.set_preshared_key(Key::zero())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Specifies an exact endpoint that this peer should be allowed to connect from.
|
/// Specifies an exact endpoint that this peer should be allowed to connect from.
|
||||||
|
#[must_use]
|
||||||
pub fn set_endpoint(mut self, address: SocketAddr) -> Self {
|
pub fn set_endpoint(mut self, address: SocketAddr) -> Self {
|
||||||
self.endpoint = Some(address);
|
self.endpoint = Some(address);
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Specifies the interval between keepalive packets to be sent to this peer.
|
/// Specifies the interval between keepalive packets to be sent to this peer.
|
||||||
|
#[must_use]
|
||||||
pub fn set_persistent_keepalive_interval(mut self, interval: u16) -> Self {
|
pub fn set_persistent_keepalive_interval(mut self, interval: u16) -> Self {
|
||||||
self.persistent_keepalive_interval = Some(interval);
|
self.persistent_keepalive_interval = Some(interval);
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Specifies that this peer does not require keepalive packets.
|
/// Specifies that this peer does not require keepalive packets.
|
||||||
|
#[must_use]
|
||||||
pub fn unset_persistent_keepalive(self) -> Self {
|
pub fn unset_persistent_keepalive(self) -> Self {
|
||||||
self.set_persistent_keepalive_interval(0)
|
self.set_persistent_keepalive_interval(0)
|
||||||
}
|
}
|
||||||
|
@ -133,6 +138,7 @@ impl PeerConfigBuilder {
|
||||||
///
|
///
|
||||||
/// See [`AllowedIp`](AllowedIp) for details. This method can be called
|
/// See [`AllowedIp`](AllowedIp) for details. This method can be called
|
||||||
/// more than once, and all IP addresses will be added to the configuration.
|
/// more than once, and all IP addresses will be added to the configuration.
|
||||||
|
#[must_use]
|
||||||
pub fn add_allowed_ip(mut self, address: IpAddr, cidr: u8) -> Self {
|
pub fn add_allowed_ip(mut self, address: IpAddr, cidr: u8) -> Self {
|
||||||
self.allowed_ips.push(AllowedIp { address, cidr });
|
self.allowed_ips.push(AllowedIp { address, cidr });
|
||||||
self
|
self
|
||||||
|
@ -142,6 +148,7 @@ impl PeerConfigBuilder {
|
||||||
///
|
///
|
||||||
/// See [`AllowedIp`](AllowedIp) for details. This method can be called
|
/// See [`AllowedIp`](AllowedIp) for details. This method can be called
|
||||||
/// more than once, and all IP addresses will be added to the configuration.
|
/// more than once, and all IP addresses will be added to the configuration.
|
||||||
|
#[must_use]
|
||||||
pub fn add_allowed_ips(mut self, ips: &[AllowedIp]) -> Self {
|
pub fn add_allowed_ips(mut self, ips: &[AllowedIp]) -> Self {
|
||||||
self.allowed_ips.extend_from_slice(ips);
|
self.allowed_ips.extend_from_slice(ips);
|
||||||
self
|
self
|
||||||
|
@ -151,6 +158,7 @@ impl PeerConfigBuilder {
|
||||||
///
|
///
|
||||||
/// This is a convenience method for cases when you want to connect to a server
|
/// This is a convenience method for cases when you want to connect to a server
|
||||||
/// that all traffic should be routed through.
|
/// that all traffic should be routed through.
|
||||||
|
#[must_use]
|
||||||
pub fn allow_all_ips(self) -> Self {
|
pub fn allow_all_ips(self) -> Self {
|
||||||
self.add_allowed_ip(IpAddr::V4(Ipv4Addr::new(0, 0, 0, 0)), 0)
|
self.add_allowed_ip(IpAddr::V4(Ipv4Addr::new(0, 0, 0, 0)), 0)
|
||||||
.add_allowed_ip(IpAddr::V6(Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 0)), 0)
|
.add_allowed_ip(IpAddr::V6(Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 0)), 0)
|
||||||
|
@ -158,12 +166,14 @@ impl PeerConfigBuilder {
|
||||||
|
|
||||||
/// Specifies that the allowed IP addresses in this configuration should replace
|
/// Specifies that the allowed IP addresses in this configuration should replace
|
||||||
/// the existing configuration of the interface, not be appended to it.
|
/// the existing configuration of the interface, not be appended to it.
|
||||||
|
#[must_use]
|
||||||
pub fn replace_allowed_ips(mut self) -> Self {
|
pub fn replace_allowed_ips(mut self) -> Self {
|
||||||
self.replace_allowed_ips = true;
|
self.replace_allowed_ips = true;
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Mark peer for removal from interface.
|
/// Mark peer for removal from interface.
|
||||||
|
#[must_use]
|
||||||
pub fn remove(mut self) -> Self {
|
pub fn remove(mut self) -> Self {
|
||||||
self.remove_me = true;
|
self.remove_me = true;
|
||||||
self
|
self
|
||||||
|
|
|
@ -150,16 +150,6 @@ impl FromStr for InterfaceName {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl InterfaceName {
|
impl InterfaceName {
|
||||||
#[cfg(target_os = "linux")]
|
|
||||||
/// Creates a new [InterfaceName](Self).
|
|
||||||
///
|
|
||||||
/// ## Safety
|
|
||||||
///
|
|
||||||
/// The caller must ensure that `name` is a valid C string terminated by a NUL.
|
|
||||||
pub(crate) unsafe fn from_wg(name: RawInterfaceName) -> Self {
|
|
||||||
Self(name)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Returns a human-readable form of the device name.
|
/// Returns a human-readable form of the device name.
|
||||||
///
|
///
|
||||||
/// Only use this when the interface name was constructed from a Rust string.
|
/// Only use this when the interface name was constructed from a Rust string.
|
||||||
|
@ -173,12 +163,6 @@ impl InterfaceName {
|
||||||
pub fn as_ptr(&self) -> *const c_char {
|
pub fn as_ptr(&self) -> *const c_char {
|
||||||
self.0.as_ptr()
|
self.0.as_ptr()
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(target_os = "linux")]
|
|
||||||
/// Consumes this interface name, returning its raw byte buffer.
|
|
||||||
pub(crate) fn into_inner(self) -> RawInterfaceName {
|
|
||||||
self.0
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl fmt::Debug for InterfaceName {
|
impl fmt::Debug for InterfaceName {
|
||||||
|
@ -307,6 +291,7 @@ pub struct DeviceUpdate {
|
||||||
|
|
||||||
impl DeviceUpdate {
|
impl DeviceUpdate {
|
||||||
/// Creates a new `DeviceConfigBuilder` that does nothing when applied.
|
/// Creates a new `DeviceConfigBuilder` that does nothing when applied.
|
||||||
|
#[must_use]
|
||||||
pub fn new() -> Self {
|
pub fn new() -> Self {
|
||||||
DeviceUpdate {
|
DeviceUpdate {
|
||||||
public_key: None,
|
public_key: None,
|
||||||
|
@ -323,40 +308,47 @@ impl DeviceUpdate {
|
||||||
/// This is a convenience method that simply wraps
|
/// This is a convenience method that simply wraps
|
||||||
/// [`set_public_key`](DeviceConfigBuilder::set_public_key)
|
/// [`set_public_key`](DeviceConfigBuilder::set_public_key)
|
||||||
/// and [`set_private_key`](DeviceConfigBuilder::set_private_key).
|
/// and [`set_private_key`](DeviceConfigBuilder::set_private_key).
|
||||||
|
#[must_use]
|
||||||
pub fn set_keypair(self, keypair: KeyPair) -> Self {
|
pub fn set_keypair(self, keypair: KeyPair) -> Self {
|
||||||
self.set_public_key(keypair.public)
|
self.set_public_key(keypair.public)
|
||||||
.set_private_key(keypair.private)
|
.set_private_key(keypair.private)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Specifies a new public key to be applied to the interface.
|
/// Specifies a new public key to be applied to the interface.
|
||||||
|
#[must_use]
|
||||||
pub fn set_public_key(mut self, key: Key) -> Self {
|
pub fn set_public_key(mut self, key: Key) -> Self {
|
||||||
self.public_key = Some(key);
|
self.public_key = Some(key);
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Specifies that the public key for this interface should be unset.
|
/// Specifies that the public key for this interface should be unset.
|
||||||
|
#[must_use]
|
||||||
pub fn unset_public_key(self) -> Self {
|
pub fn unset_public_key(self) -> Self {
|
||||||
self.set_public_key(Key::zero())
|
self.set_public_key(Key::zero())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Sets a new private key to be applied to the interface.
|
/// Sets a new private key to be applied to the interface.
|
||||||
|
#[must_use]
|
||||||
pub fn set_private_key(mut self, key: Key) -> Self {
|
pub fn set_private_key(mut self, key: Key) -> Self {
|
||||||
self.private_key = Some(key);
|
self.private_key = Some(key);
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Specifies that the private key for this interface should be unset.
|
/// Specifies that the private key for this interface should be unset.
|
||||||
|
#[must_use]
|
||||||
pub fn unset_private_key(self) -> Self {
|
pub fn unset_private_key(self) -> Self {
|
||||||
self.set_private_key(Key::zero())
|
self.set_private_key(Key::zero())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Specifies the fwmark value that should be applied to packets coming from the interface.
|
/// Specifies the fwmark value that should be applied to packets coming from the interface.
|
||||||
|
#[must_use]
|
||||||
pub fn set_fwmark(mut self, fwmark: u32) -> Self {
|
pub fn set_fwmark(mut self, fwmark: u32) -> Self {
|
||||||
self.fwmark = Some(fwmark);
|
self.fwmark = Some(fwmark);
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Specifies that fwmark should not be set on packets from the interface.
|
/// Specifies that fwmark should not be set on packets from the interface.
|
||||||
|
#[must_use]
|
||||||
pub fn unset_fwmark(self) -> Self {
|
pub fn unset_fwmark(self) -> Self {
|
||||||
self.set_fwmark(0)
|
self.set_fwmark(0)
|
||||||
}
|
}
|
||||||
|
@ -364,6 +356,7 @@ impl DeviceUpdate {
|
||||||
/// Specifies the port to listen for incoming packets on.
|
/// Specifies the port to listen for incoming packets on.
|
||||||
///
|
///
|
||||||
/// This is useful for a server configuration that listens on a fixed endpoint.
|
/// This is useful for a server configuration that listens on a fixed endpoint.
|
||||||
|
#[must_use]
|
||||||
pub fn set_listen_port(mut self, port: u16) -> Self {
|
pub fn set_listen_port(mut self, port: u16) -> Self {
|
||||||
self.listen_port = Some(port);
|
self.listen_port = Some(port);
|
||||||
self
|
self
|
||||||
|
@ -372,6 +365,7 @@ impl DeviceUpdate {
|
||||||
/// Specifies that a random port should be used for incoming packets.
|
/// Specifies that a random port should be used for incoming packets.
|
||||||
///
|
///
|
||||||
/// This is probably what you want in client configurations.
|
/// This is probably what you want in client configurations.
|
||||||
|
#[must_use]
|
||||||
pub fn randomize_listen_port(self) -> Self {
|
pub fn randomize_listen_port(self) -> Self {
|
||||||
self.set_listen_port(0)
|
self.set_listen_port(0)
|
||||||
}
|
}
|
||||||
|
@ -381,6 +375,7 @@ impl DeviceUpdate {
|
||||||
/// See [`PeerConfigBuilder`](PeerConfigBuilder) for details on building
|
/// See [`PeerConfigBuilder`](PeerConfigBuilder) for details on building
|
||||||
/// peer configurations. This method can be called more than once, and all
|
/// peer configurations. This method can be called more than once, and all
|
||||||
/// peers will be added to the configuration.
|
/// peers will be added to the configuration.
|
||||||
|
#[must_use]
|
||||||
pub fn add_peer(mut self, peer: PeerConfigBuilder) -> Self {
|
pub fn add_peer(mut self, peer: PeerConfigBuilder) -> Self {
|
||||||
self.peers.push(peer);
|
self.peers.push(peer);
|
||||||
self
|
self
|
||||||
|
@ -391,6 +386,7 @@ impl DeviceUpdate {
|
||||||
/// This is simply a convenience method to make adding peers more fluent.
|
/// This is simply a convenience method to make adding peers more fluent.
|
||||||
/// This method can be called more than once, and all peers will be added
|
/// This method can be called more than once, and all peers will be added
|
||||||
/// to the configuration.
|
/// to the configuration.
|
||||||
|
#[must_use]
|
||||||
pub fn add_peer_with(
|
pub fn add_peer_with(
|
||||||
self,
|
self,
|
||||||
pubkey: &Key,
|
pubkey: &Key,
|
||||||
|
@ -400,6 +396,7 @@ impl DeviceUpdate {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Specifies multiple peer configurations to be added to the interface.
|
/// Specifies multiple peer configurations to be added to the interface.
|
||||||
|
#[must_use]
|
||||||
pub fn add_peers(mut self, peers: &[PeerConfigBuilder]) -> Self {
|
pub fn add_peers(mut self, peers: &[PeerConfigBuilder]) -> Self {
|
||||||
self.peers.extend_from_slice(peers);
|
self.peers.extend_from_slice(peers);
|
||||||
self
|
self
|
||||||
|
@ -407,12 +404,14 @@ impl DeviceUpdate {
|
||||||
|
|
||||||
/// Specifies that the peer configurations in this `DeviceConfigBuilder` should
|
/// Specifies that the peer configurations in this `DeviceConfigBuilder` should
|
||||||
/// replace the existing configurations on the interface, not modify or append to them.
|
/// replace the existing configurations on the interface, not modify or append to them.
|
||||||
|
#[must_use]
|
||||||
pub fn replace_peers(mut self) -> Self {
|
pub fn replace_peers(mut self) -> Self {
|
||||||
self.replace_peers = true;
|
self.replace_peers = true;
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Specifies that the peer with this public key should be removed from the interface.
|
/// Specifies that the peer with this public key should be removed from the interface.
|
||||||
|
#[must_use]
|
||||||
pub fn remove_peer_by_key(self, public_key: &Key) -> Self {
|
pub fn remove_peer_by_key(self, public_key: &Key) -> Self {
|
||||||
let mut peer = PeerConfigBuilder::new(public_key);
|
let mut peer = PeerConfigBuilder::new(public_key);
|
||||||
peer.remove_me = true;
|
peer.remove_me = true;
|
||||||
|
|
|
@ -1,4 +1,3 @@
|
||||||
use crate::backends;
|
|
||||||
use std::{ffi::NulError, fmt};
|
use std::{ffi::NulError, fmt};
|
||||||
|
|
||||||
/// Represents an error in base64 key parsing.
|
/// Represents an error in base64 key parsing.
|
||||||
|
@ -27,11 +26,122 @@ impl From<NulError> for InvalidKey {
|
||||||
///
|
///
|
||||||
/// This means that you need to be careful when working with
|
/// This means that you need to be careful when working with
|
||||||
/// `Key`s, especially ones created from external data.
|
/// `Key`s, especially ones created from external data.
|
||||||
#[cfg(not(target_os = "linux"))]
|
#[derive(PartialEq, Eq, Clone)]
|
||||||
pub use backends::userspace::Key;
|
pub struct Key(pub [u8; 32]);
|
||||||
|
|
||||||
#[cfg(target_os = "linux")]
|
impl Key {
|
||||||
pub use backends::kernel::Key;
|
/// Generates and returns a new private key.
|
||||||
|
pub fn generate_private() -> Self {
|
||||||
|
use rand_core::{OsRng, RngCore};
|
||||||
|
|
||||||
|
let mut bytes = [0u8; 32];
|
||||||
|
OsRng.fill_bytes(&mut bytes);
|
||||||
|
|
||||||
|
// Apply key clamping.
|
||||||
|
bytes[0] &= 248;
|
||||||
|
bytes[31] &= 127;
|
||||||
|
bytes[31] |= 64;
|
||||||
|
Self(bytes)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Generates and returns a new preshared key.
|
||||||
|
#[must_use]
|
||||||
|
pub fn generate_preshared() -> Self {
|
||||||
|
use rand_core::{OsRng, RngCore};
|
||||||
|
|
||||||
|
let mut key = [0u8; 32];
|
||||||
|
OsRng.fill_bytes(&mut key);
|
||||||
|
Self(key)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Generates a public key for this private key.
|
||||||
|
#[must_use]
|
||||||
|
pub fn generate_public(&self) -> Self {
|
||||||
|
use curve25519_dalek::scalar::Scalar;
|
||||||
|
|
||||||
|
use curve25519_dalek::constants::ED25519_BASEPOINT_TABLE;
|
||||||
|
|
||||||
|
// https://github.com/dalek-cryptography/x25519-dalek/blob/1c39ff92e0dfc0b24aa02d694f26f3b9539322a5/src/x25519.rs#L150
|
||||||
|
let point = (&ED25519_BASEPOINT_TABLE * &Scalar::from_bits(self.0)).to_montgomery();
|
||||||
|
|
||||||
|
Self(point.to_bytes())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Generates an all-zero key.
|
||||||
|
#[must_use]
|
||||||
|
pub fn zero() -> Self {
|
||||||
|
Self([0u8; 32])
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn as_bytes(&self) -> &[u8] {
|
||||||
|
&self.0
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Converts the key to a standardized base64 representation, as used by the `wg` utility and `wg-quick`.
|
||||||
|
pub fn to_base64(&self) -> String {
|
||||||
|
base64::encode(&self.0)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Converts a base64 representation of the key to the raw bytes.
|
||||||
|
///
|
||||||
|
/// This can fail, as not all text input is valid base64 - in this case
|
||||||
|
/// `Err(InvalidKey)` is returned.
|
||||||
|
pub fn from_base64(key: &str) -> Result<Self, crate::InvalidKey> {
|
||||||
|
let mut key_bytes = [0u8; 32];
|
||||||
|
let decoded_bytes = base64::decode(key).map_err(|_| InvalidKey)?;
|
||||||
|
|
||||||
|
if decoded_bytes.len() != 32 {
|
||||||
|
return Err(InvalidKey);
|
||||||
|
}
|
||||||
|
|
||||||
|
key_bytes.copy_from_slice(&decoded_bytes[..]);
|
||||||
|
Ok(Self(key_bytes))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn from_hex(hex_str: &str) -> Result<Self, crate::InvalidKey> {
|
||||||
|
let mut sized_bytes = [0u8; 32];
|
||||||
|
hex::decode_to_slice(hex_str, &mut sized_bytes).map_err(|_| InvalidKey)?;
|
||||||
|
Ok(Self(sized_bytes))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod test {
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_pubkey_generation() {
|
||||||
|
let privkey = "SGb+ojrRNDuMePufwtIYhXzA//k6wF3R21tEBgKlzlM=";
|
||||||
|
let pubkey = "DD5yKRfzExcV5+kDnTroDgCU15latdMjiQ59j1hEuk8=";
|
||||||
|
|
||||||
|
let private = Key::from_base64(privkey).unwrap();
|
||||||
|
let public = Key::generate_public(&private);
|
||||||
|
|
||||||
|
assert_eq!(public.to_base64(), pubkey);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_rng_sanity_private() {
|
||||||
|
let first = Key::generate_private();
|
||||||
|
assert!(first.as_bytes() != [0u8; 32]);
|
||||||
|
for _ in 0..100_000 {
|
||||||
|
let key = Key::generate_private();
|
||||||
|
assert!(first != key);
|
||||||
|
assert!(key.as_bytes() != [0u8; 32]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_rng_sanity_preshared() {
|
||||||
|
let first = Key::generate_preshared();
|
||||||
|
assert!(first.as_bytes() != [0u8; 32]);
|
||||||
|
for _ in 0..100_000 {
|
||||||
|
let key = Key::generate_preshared();
|
||||||
|
assert!(first != key);
|
||||||
|
assert!(key.as_bytes() != [0u8; 32]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Represents a pair of private and public keys.
|
/// Represents a pair of private and public keys.
|
||||||
///
|
///
|
||||||
|
|
Loading…
Reference in New Issue