mirror of
https://github.com/zaphar/durnitisp.git
synced 2025-07-23 18:29:49 -04:00
Export ping statistics to prometheus
This commit is contained in:
parent
13889a2a04
commit
368ae10e0f
124
Cargo.lock
generated
124
Cargo.lock
generated
@ -35,12 +35,6 @@ version = "1.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f8aac770f1885fd7e387acedd76065302551364496e46b3dd00860b2f8359b9d"
|
||||
|
||||
[[package]]
|
||||
name = "bitflags"
|
||||
version = "1.2.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "cf1de2fe8c75bc145a2f577add951f8134889b4795d47466a54a5c846d691693"
|
||||
|
||||
[[package]]
|
||||
name = "byteorder"
|
||||
version = "1.3.4"
|
||||
@ -91,22 +85,44 @@ name = "durnitisp"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"ekko",
|
||||
"gflags",
|
||||
"log",
|
||||
"nursery",
|
||||
"packet",
|
||||
"prometheus",
|
||||
"socket2",
|
||||
"stderrlog",
|
||||
"tiny_http",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ekko"
|
||||
version = "0.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f867af1a5d502e71ac40aa6204f057bda420d36eb3a8d6f1eb3a9e16f17b3184"
|
||||
dependencies = [
|
||||
"byteorder",
|
||||
"rand",
|
||||
"socket2",
|
||||
"thiserror",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "fnv"
|
||||
version = "1.0.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1"
|
||||
|
||||
[[package]]
|
||||
name = "getrandom"
|
||||
version = "0.1.15"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "fc587bc0ec293155d5bfa6b9891ec18a1e330c234f896ea47fbada4cadbe47e6"
|
||||
dependencies = [
|
||||
"cfg-if 0.1.10",
|
||||
"libc",
|
||||
"wasi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "gflags"
|
||||
version = "0.3.5"
|
||||
@ -141,15 +157,6 @@ dependencies = [
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "hwaddr"
|
||||
version = "0.1.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e414433a9e4338f4e87fa29d0670c883a5e73e7955c45f4a49130c0aa992c85b"
|
||||
dependencies = [
|
||||
"phf",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "idna"
|
||||
version = "0.2.0"
|
||||
@ -247,18 +254,6 @@ version = "0.0.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a4bd2d4e0cd7c6bb256afbc59a5921c3ead56f05d7696c92e05b6978858b6fa5"
|
||||
|
||||
[[package]]
|
||||
name = "packet"
|
||||
version = "0.1.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c136c7ad0619ed4f88894aecf66ad86c80683e7b5d707996e6a3a7e0e3916944"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
"byteorder",
|
||||
"hwaddr",
|
||||
"thiserror",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "percent-encoding"
|
||||
version = "2.1.0"
|
||||
@ -266,22 +261,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d4fd5641d01c8f18a23da7b6fe29298ff4b55afcccdf78973b24cf3175fee32e"
|
||||
|
||||
[[package]]
|
||||
name = "phf"
|
||||
version = "0.8.0"
|
||||
name = "ppv-lite86"
|
||||
version = "0.2.10"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3dfb61232e34fcb633f43d12c58f83c1df82962dcdfa565a4e866ffc17dafe12"
|
||||
dependencies = [
|
||||
"phf_shared",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "phf_shared"
|
||||
version = "0.8.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c00cf8b9eafe68dde5e9eaa2cef8ee84a9336a47d566ec55ca16589633b65af7"
|
||||
dependencies = [
|
||||
"siphasher",
|
||||
]
|
||||
checksum = "ac74c624d6b2d21f425f752262f42188365d7b8ff1aff74c82e45136510a4857"
|
||||
|
||||
[[package]]
|
||||
name = "proc-macro2"
|
||||
@ -321,6 +304,47 @@ dependencies = [
|
||||
"proc-macro2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rand"
|
||||
version = "0.7.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6a6b1679d49b24bbfe0c803429aa1874472f50d9b363131f0e89fc356b544d03"
|
||||
dependencies = [
|
||||
"getrandom",
|
||||
"libc",
|
||||
"rand_chacha",
|
||||
"rand_core",
|
||||
"rand_hc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rand_chacha"
|
||||
version = "0.2.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f4c8ed856279c9737206bf725bf36935d8666ead7aa69b52be55af369d193402"
|
||||
dependencies = [
|
||||
"ppv-lite86",
|
||||
"rand_core",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rand_core"
|
||||
version = "0.5.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "90bde5296fc891b0cef12a6d03ddccc162ce7b2aff54160af9338f8d40df6d19"
|
||||
dependencies = [
|
||||
"getrandom",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rand_hc"
|
||||
version = "0.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ca3129af7b92a17112d59ad498c6f81eaf463253766b90396d39ea7a39d6613c"
|
||||
dependencies = [
|
||||
"rand_core",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "redox_syscall"
|
||||
version = "0.1.57"
|
||||
@ -356,12 +380,6 @@ dependencies = [
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "siphasher"
|
||||
version = "0.3.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "fa8f3741c7372e75519bd9346068370c9cdaabcc1f9599cbcf2a2719352286b7"
|
||||
|
||||
[[package]]
|
||||
name = "socket2"
|
||||
version = "0.3.19"
|
||||
@ -533,6 +551,12 @@ version = "1.0.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6a02e4885ed3bc0f2de90ea6dd45ebcbb66dacffe03547fadbb0eeae2770887d"
|
||||
|
||||
[[package]]
|
||||
name = "wasi"
|
||||
version = "0.9.0+wasi-snapshot-preview1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "cccddf32554fecc6acb585f82a32a72e28b48f8c4c1883ddfeeeaa96f7d8e519"
|
||||
|
||||
[[package]]
|
||||
name = "winapi"
|
||||
version = "0.3.9"
|
||||
|
@ -14,5 +14,4 @@ nursery = "^0.0.1"
|
||||
prometheus = "^0.9.0"
|
||||
stderrlog = "0.4"
|
||||
tiny_http = "^0.7.0"
|
||||
packet = "^0.1.4"
|
||||
socket2 = "^0.3.19"
|
||||
ekko = "0.2.0"
|
139
src/icmp.rs
139
src/icmp.rs
@ -11,72 +11,41 @@
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
use std::convert::Into;
|
||||
use std::net::{IpAddr, SocketAddr};
|
||||
use std::sync::{Arc, RwLock};
|
||||
use std::time::{Duration, Instant};
|
||||
|
||||
use ekko::{Ekko, EkkoResponse};
|
||||
use gflags;
|
||||
use log::{debug, error, info};
|
||||
use packet::icmp::echo::{Builder, Packet};
|
||||
use packet::Builder as PBuilder;
|
||||
use socket2::Domain;
|
||||
use socket2::Protocol;
|
||||
use socket2::SockAddr;
|
||||
use socket2::Socket;
|
||||
use log::{error, info};
|
||||
use prometheus::{CounterVec, IntGaugeVec};
|
||||
use std::sync::{Arc, RwLock};
|
||||
use std::time::Duration;
|
||||
|
||||
gflags::define! {
|
||||
// The size in bytes of the ping requests.
|
||||
--pingPayload = "durnitisp ping test"
|
||||
--pingPayload = "durnitisp"
|
||||
}
|
||||
|
||||
fn make_echo_packet(ident: u16) -> Packet<Vec<u8>> {
|
||||
let buffer = Builder::default()
|
||||
.request()
|
||||
.unwrap()
|
||||
.identifier(ident)
|
||||
.unwrap()
|
||||
.sequence(0)
|
||||
.unwrap()
|
||||
.payload(PINGPAYLOAD.flag.as_bytes())
|
||||
.unwrap()
|
||||
.build()
|
||||
.unwrap();
|
||||
Packet::unchecked(buffer)
|
||||
gflags::define! {
|
||||
// The size in bytes of the ping requests.
|
||||
--pingTTL: u32 = 113
|
||||
}
|
||||
|
||||
gflags::define! {
|
||||
// The size in bytes of the ping requests.
|
||||
--pingTimeout: u64 = 2048
|
||||
}
|
||||
|
||||
gflags::define! {
|
||||
// The size in bytes of the ping requests.
|
||||
--maxHops: u8 = 50
|
||||
}
|
||||
|
||||
pub fn start_echo_loop(
|
||||
domain_name: &str,
|
||||
stop_signal: Arc<RwLock<bool>>,
|
||||
addr: IpAddr,
|
||||
ident: u16,
|
||||
ping_latency_guage: IntGaugeVec,
|
||||
ping_counter: CounterVec,
|
||||
) {
|
||||
info!("Starting ping of {}", domain_name);
|
||||
// First we construct our icmp transport
|
||||
// TODO(jwall): Timeouts.
|
||||
// TODO(jwall): Handle out of order packets.
|
||||
let (domain, protocol) = match addr {
|
||||
IpAddr::V4(_) => (Domain::ipv4(), Protocol::icmpv4()),
|
||||
IpAddr::V6(_) => (Domain::ipv6(), Protocol::icmpv6()),
|
||||
};
|
||||
// Construct a socket to send the ICMP request on.
|
||||
// socket type: Ip, Datagram, ICMP
|
||||
let addr: SocketAddr = (addr, 0).into();
|
||||
let addr: SockAddr = addr.into();
|
||||
let socket = match Socket::new(domain, socket2::Type::raw(), Some(protocol)) {
|
||||
Ok(s) => s,
|
||||
Err(e) => {
|
||||
error!("Unable to create socket for icmp request:\n {}", e);
|
||||
return;
|
||||
}
|
||||
};
|
||||
|
||||
socket
|
||||
.set_read_timeout(Some(Duration::from_millis(2048)))
|
||||
.unwrap();
|
||||
// then we start our loop
|
||||
let mut n = 0;
|
||||
let mut pkt = make_echo_packet(ident);
|
||||
info!("Pinging {}", domain_name);
|
||||
let mut sender = Ekko::with_target(domain_name).unwrap();
|
||||
loop {
|
||||
{
|
||||
// Limit the scope of this lock
|
||||
@ -85,41 +54,35 @@ pub fn start_echo_loop(
|
||||
return;
|
||||
}
|
||||
}
|
||||
pkt.set_sequence(n).unwrap();
|
||||
// TODO(jwall): Count the errors?
|
||||
// construct echo packet
|
||||
let time_of_send = Instant::now();
|
||||
// send echo packet
|
||||
let pkt_buf: &[u8] = pkt.as_ref();
|
||||
debug!("Sending echo request for {}", domain_name);
|
||||
let sent = socket.send_to(pkt_buf, &addr).unwrap();
|
||||
if pkt_buf.len() != sent {
|
||||
error!("Failed to send a complete icmp packet!");
|
||||
continue;
|
||||
}
|
||||
// // Wait for echo response
|
||||
debug!("Waiting for echo reply from {}", domain_name);
|
||||
let mut buf = vec![0; sent];
|
||||
let _rcv_size = match socket.recv(&mut buf) {
|
||||
Ok(sz) => sz,
|
||||
Err(e) => {
|
||||
if let std::io::ErrorKind::TimedOut = e.kind() {
|
||||
error!("icmp echo request timed out to {}", domain_name);
|
||||
continue;
|
||||
}
|
||||
error!("Error recieving on icmp socket! {:?}", e);
|
||||
return;
|
||||
let response = sender
|
||||
.send_with_timeout(MAXHOPS.flag, Some(Duration::from_millis(PINGTIMEOUT.flag)))
|
||||
.unwrap();
|
||||
match response {
|
||||
EkkoResponse::DestinationResponse(r) => {
|
||||
info!(
|
||||
"ICMP: Reply from {}: time={}ms",
|
||||
r.address.unwrap(),
|
||||
r.elapsed.as_millis(),
|
||||
);
|
||||
ping_counter
|
||||
.with(&prometheus::labels! {"result" => "ok", "domain" => domain_name})
|
||||
.inc();
|
||||
ping_latency_guage
|
||||
.with(&prometheus::labels! {"domain" => domain_name})
|
||||
.set(r.elapsed.as_millis() as i64);
|
||||
}
|
||||
EkkoResponse::ExceededResponse(r) => {
|
||||
ping_counter
|
||||
.with(&prometheus::labels! {"result" => "timedout", "domain" => domain_name})
|
||||
.inc();
|
||||
}
|
||||
_ => {
|
||||
ping_counter
|
||||
.with(&prometheus::labels! {"result" => "err", "domain" => domain_name})
|
||||
.inc();
|
||||
error!("{:?}", response);
|
||||
}
|
||||
};
|
||||
let echo = Packet::new(&buf).unwrap();
|
||||
if echo.sequence() == n {
|
||||
let round_trip_time = Instant::now().checked_duration_since(time_of_send).unwrap();
|
||||
// record this time
|
||||
info!("Sequence # {} {}ms", n, round_trip_time.as_millis());
|
||||
} else {
|
||||
error!("Got the wrong sequence number {}", echo.sequence());
|
||||
}
|
||||
// Increment our sequence number
|
||||
n += 1;
|
||||
std::thread::sleep(Duration::from_secs(3));
|
||||
}
|
||||
}
|
||||
|
58
src/main.rs
58
src/main.rs
@ -88,35 +88,50 @@ fn main() -> anyhow::Result<()> {
|
||||
}
|
||||
// FIXME(jwall): allow them to override ping hosts
|
||||
let ping_hosts = default_ping_hosts;
|
||||
let counter_opts = Opts::new(
|
||||
"stun_attempt_counter",
|
||||
"Counter for the good, bad, and total attempts to connect to stun server.",
|
||||
);
|
||||
let gauge_opts = Opts::new(
|
||||
"stun_attempt_latency_ms",
|
||||
"Latency guage in millis per stun domain.",
|
||||
);
|
||||
|
||||
let stop_signal = Arc::new(RwLock::new(false));
|
||||
|
||||
// Create a Registry and register metrics.
|
||||
let r = Registry::new();
|
||||
let stun_counter_vec = CounterVec::new(counter_opts, &["result", "domain"]).unwrap();
|
||||
let stun_counter_vec = CounterVec::new(
|
||||
Opts::new(
|
||||
"stun_attempt_counter",
|
||||
"Counter for the good, bad, and total attempts to connect to stun server.",
|
||||
),
|
||||
&["result", "domain"],
|
||||
)
|
||||
.unwrap();
|
||||
let stun_success_vec = IntGaugeVec::new(
|
||||
Opts::new("stun_success", "Stun probe successes"),
|
||||
&["domain"],
|
||||
)
|
||||
.unwrap();
|
||||
let stun_latency_vec = IntGaugeVec::new(
|
||||
Opts::new(
|
||||
"stun_attempt_latency_ms",
|
||||
"Latency guage in millis per stun domain.",
|
||||
),
|
||||
&["domain"],
|
||||
)
|
||||
.unwrap();
|
||||
let ping_latency_vec =
|
||||
IntGaugeVec::new(Opts::new("ping_latency", "ICMP Ping latency"), &["domain"]).unwrap();
|
||||
let ping_counter_vec = CounterVec::new(
|
||||
Opts::new("ping_counter", "Ping Request Counter"),
|
||||
&["result", "domain"],
|
||||
)
|
||||
.unwrap();
|
||||
r.register(Box::new(stun_counter_vec.clone()))
|
||||
.expect("Failed to register stun connection counter");
|
||||
let stun_latency_vec = IntGaugeVec::new(gauge_opts, &["domain"]).unwrap();
|
||||
r.register(Box::new(stun_latency_vec.clone()))
|
||||
.expect("Failed to register stun latency guage");
|
||||
r.register(Box::new(stun_success_vec.clone()))
|
||||
.expect("Failed to register stun success gauge");
|
||||
r.register(Box::new(ping_latency_vec.clone()))
|
||||
.expect("Failed to register ping latency guage");
|
||||
r.register(Box::new(ping_counter_vec.clone()))
|
||||
.expect("Failed to register ping counter");
|
||||
let stun_socket_addrs = util::resolve_addrs(&stun_servers).unwrap();
|
||||
let stun_servers = Arc::new(stun_servers);
|
||||
let ping_addrs = util::resolve_ip_addrs(&ping_hosts).unwrap();
|
||||
let ping_hosts = Arc::new(ping_hosts);
|
||||
|
||||
let mut parent = Nursery::new();
|
||||
@ -161,18 +176,15 @@ fn main() -> anyhow::Result<()> {
|
||||
});
|
||||
parent.adopt(Box::new(render_thread));
|
||||
}
|
||||
for (i, addr) in ping_addrs.iter().cloned().enumerate() {
|
||||
for (i, domain_name) in ping_hosts.iter().cloned().enumerate() {
|
||||
// TODO(Prometheus stats)
|
||||
let ping_hosts_copy = ping_hosts.clone();
|
||||
if let Some(addr) = dbg!(addr) {
|
||||
let domain_name = *ping_hosts_copy.get(i).unwrap();
|
||||
debug!("Pinging {}", domain_name);
|
||||
let stop_signal = stop_signal.clone();
|
||||
let ping_thread = thread::Pending::new(move || {
|
||||
icmp::start_echo_loop(domain_name, stop_signal, addr, i as u16);
|
||||
});
|
||||
parent.schedule(Box::new(ping_thread));
|
||||
}
|
||||
let stop_signal = stop_signal.clone();
|
||||
let ping_latency_vec = ping_latency_vec.clone();
|
||||
let ping_counter_vec = ping_counter_vec.clone();
|
||||
let ping_thread = thread::Pending::new(move || {
|
||||
icmp::start_echo_loop(domain_name, stop_signal, ping_latency_vec, ping_counter_vec);
|
||||
});
|
||||
parent.schedule(Box::new(ping_thread));
|
||||
}
|
||||
// Then we attempt to start connections to each stun server.
|
||||
for (i, s) in stun_socket_addrs.iter().enumerate() {
|
||||
|
Loading…
x
Reference in New Issue
Block a user