Repository: m-ou-se/pong
Branch: main
Commit: 8bd35623b64e
Files: 5
Total size: 11.8 KB
Directory structure:
gitextract_kd58c96m/
├── .gitignore
├── Cargo.toml
├── README.md
└── src/
├── disable_system_pong.rs
└── main.rs
================================================
FILE CONTENTS
================================================
================================================
FILE: .gitignore
================================================
/target
================================================
FILE: Cargo.toml
================================================
[package]
name = "pong"
version = "0.1.0"
edition = "2021"
[dependencies]
anyhow = "1.0.52"
libc = "0.2.112"
nix = "0.23.1"
signal-hook = "0.3.13"
================================================
FILE: README.md
================================================
# Pong
A Linux program that replies to `ping` but modifies the payload of the ICMP
packet to get lower ping times in some `ping` implementations.
See
- https://twitter.com/m_ou_se/status/1480184730058375176
- https://twitter.com/m_ou_se/status/1480184732562374656
- https://twitter.com/m_ou_se/status/1480188334605578242
Install it with `cargo install --git https://github.com/m-ou-se/pong`.
You either need to run it as root, or you need to disable your kernel's ping
reply with `sysctl net.ipv4.icmp_echo_ignore_all=1 net.ipv6.icmp.echo_ignore_all=1`
and give this program `cap_net_raw` capabilities with `setcap cap_net_raw=ep ~/.cargo/bin/pong`.
================================================
FILE: src/disable_system_pong.rs
================================================
use anyhow::{bail, Context, Result};
use std::fs::{read, write};
const PATH_IPV4: &str = "/proc/sys/net/ipv4/icmp_echo_ignore_all";
const PATH_IPV6: &str = "/proc/sys/net/ipv6/icmp/echo_ignore_all";
#[must_use]
pub struct DisableSystemPong {
reenable_ipv4_on_drop: bool,
reenable_ipv6_on_drop: bool,
}
impl DisableSystemPong {
pub fn activate() -> Result<Self> {
Ok(DisableSystemPong {
reenable_ipv4_on_drop: if read(PATH_IPV4)
.with_context(|| format!("unable to read {}", PATH_IPV4))?
== b"1\n"
{
// Already disabled.
false
} else {
if write(PATH_IPV4, "1\n").is_err() {
bail!(
"unable to disable the system's IPv4 ICMP echo reply\n\n\
Disable it manually (using `sysctl net.ipv4.icmp_echo_ignore_all=1`), \
or re-run this program as root."
);
}
eprintln!("disabled the system's IPv4 ICMP echo reply");
true
},
reenable_ipv6_on_drop: if read(PATH_IPV6)
.with_context(|| format!("unable to read {}", PATH_IPV6))?
== b"1\n"
{
// Already disabled.
false
} else {
if write(PATH_IPV6, "1\n").is_err() {
bail!(
"unable to disable the system's IPv6 ICMP echo reply\n\n\
Disable it manually (using `sysctl net.ipv6.icmp.echo_ignore_all=1`), \
or re-run this program as root."
);
}
eprintln!("disabled the system's IPv6 ICMP echo reply");
true
},
})
}
pub fn deactivate(&mut self) -> Result<()> {
if self.reenable_ipv4_on_drop {
self.reenable_ipv4_on_drop = false;
write(PATH_IPV4, "0\n")
.context("unable to re-enable the system's IPv4 ICMP echo reply")?;
eprintln!("re-enabled the system's IPv4 ICMP echo reply");
}
if self.reenable_ipv6_on_drop {
self.reenable_ipv6_on_drop = false;
write(PATH_IPV6, "0\n")
.context("unable to re-enable the system's IPv6 ICMP echo reply")?;
eprintln!("re-enabled the system's IPv6 ICMP echo reply");
}
Ok(())
}
}
impl Drop for DisableSystemPong {
fn drop(&mut self) {
let _ = self.deactivate();
}
}
================================================
FILE: src/main.rs
================================================
#[cfg(not(target_os = "linux"))]
compile_error!("this program only works on Linux");
mod disable_system_pong;
use anyhow::{bail, Context, Result};
use disable_system_pong::DisableSystemPong;
use libc::{socket, AF_INET, AF_INET6, IPPROTO_ICMP, IPPROTO_ICMPV6, SOCK_RAW};
use nix::errno::Errno;
use nix::poll::{poll, PollFd, PollFlags};
use std::io::ErrorKind;
use std::net::UdpSocket;
use std::os::unix::io::{AsRawFd, FromRawFd};
use std::os::unix::net::UnixStream;
use std::time::{Duration, SystemTime};
// It's not really a UdpSocket, but there's no IcmpSocket in std and the interface is close enough. :)
type IcmpSocket = UdpSocket;
fn open_icmp_socket(ipv6: bool) -> Result<IcmpSocket> {
let sock = unsafe {
if ipv6 {
socket(AF_INET6, SOCK_RAW, IPPROTO_ICMPV6)
} else {
socket(AF_INET, SOCK_RAW, IPPROTO_ICMP)
}
};
if sock == -1 {
let err = std::io::Error::last_os_error();
if err.kind() == ErrorKind::PermissionDenied {
bail!(
"unable to create a raw {} ICMP socket\n\n\
Re-run this program as root or with cap_net_raw capabilities \
(using `setcap cap_net_raw=ep {:?}`).",
if ipv6 { "IPv6" } else { "IPv4" },
std::env::args_os().next().unwrap()
);
} else {
return Err(err).context("creating raw ICMP socket failed");
}
}
Ok(unsafe { IcmpSocket::from_raw_fd(sock) })
}
fn main() -> Result<()> {
let (stop_read, stop_write) = UnixStream::pair()?;
for &signal in signal_hook::consts::TERM_SIGNALS {
signal_hook::low_level::pipe::register(signal, stop_write.try_clone()?)?;
}
let mut disable_pong = DisableSystemPong::activate()?;
let sock4 = open_icmp_socket(false)?;
let sock6 = open_icmp_socket(true)?;
let mut buf = [0; 1024];
loop {
let mut fds = [
PollFd::new(sock4.as_raw_fd(), PollFlags::POLLIN),
PollFd::new(sock6.as_raw_fd(), PollFlags::POLLIN),
PollFd::new(stop_read.as_raw_fd(), PollFlags::POLLIN),
];
match poll(&mut fds, -1) {
Ok(_) => {}
Err(e) if e == Errno::EINTR => {}
Err(e) => return Err(e).context("polling the sockets failed"),
}
if fds[2].revents().unwrap().contains(PollFlags::POLLIN) {
// Got signal. Exiting.
break;
}
let now = SystemTime::now()
.duration_since(SystemTime::UNIX_EPOCH)
.unwrap();
for (ipv6, fd, sock) in [(false, fds[0], &sock4), (true, fds[1], &sock6)] {
if fd.revents().unwrap().contains(PollFlags::POLLIN) {
let ip_header_len = if ipv6 { 0 } else { 20 };
let (len, addr) = sock.recv_from(&mut buf).context("recv_from failed")?;
if len < ip_header_len + 8 {
continue;
}
let icmp = &mut buf[ip_header_len..len];
let (req_type, reply_type) = if ipv6 { (128, 129) } else { (8, 0) };
if icmp[0..2] != [req_type, 0] {
// Not a ping packet.
continue;
}
let id = u16::from_be_bytes(icmp[4..6].try_into().unwrap());
let seq = u16::from_be_bytes(icmp[6..8].try_into().unwrap());
let parsed = parse_payload(now, &icmp[8..]);
// Transform ping packet into pong packet
icmp[0] = reply_type;
icmp[1] = 0;
// Change the payload to get better ping times.
if let Some((encoding, timestamp)) = parsed {
let new_timestamp =
if timestamp < now && (now - timestamp) < Duration::from_millis(500) {
// Looks like the sender is ntp-synchronized.
// Calculate the arrival time, minus five milliseconds to make it somewhat realistic.
now + (now - timestamp) - Duration::from_millis(5)
} else {
// The sender's clock isn't the same as ours.
// Just decrease the ping time by 50ms.
timestamp + Duration::from_millis(50)
};
write_timestamp_into_payload(&mut icmp[8..], encoding, new_timestamp);
} else {
print!("unknown encoding for ping payload: ");
for &b in &icmp[8..] {
print!("{:02x} ", b);
}
println!();
}
// Update the checksum
let checksum = checksum(&icmp[4..]);
icmp[2..4].copy_from_slice(&checksum.to_be_bytes());
// Pong!
sock.send_to(icmp, addr).context("send_to failed")?;
println!(
"{}: {} bytes, id={}, seq={}, encoding={}",
addr.ip(),
icmp.len(),
id,
seq,
match parsed {
Some((PayloadEncoding::Le64, _)) => "le64",
Some((PayloadEncoding::Be64, _)) => "be64",
Some((PayloadEncoding::Le32, _)) => "le32",
Some((PayloadEncoding::Be32, _)) => "be32",
None => "unknown",
}
);
}
}
}
disable_pong.deactivate()?;
Ok(())
}
#[derive(Copy, Clone, PartialEq, Eq, Debug)]
enum PayloadEncoding {
Le64,
Be64,
Le32,
Be32,
}
fn parse_payload(now: Duration, payload: &[u8]) -> Option<(PayloadEncoding, Duration)> {
if payload.len() >= 16 {
let sec = u64::from_le_bytes(payload[0..8].try_into().unwrap());
if sec.abs_diff(now.as_secs()) < 1000 && payload[12..16] == [0; 4] {
let usec = u32::from_le_bytes(payload[8..12].try_into().unwrap());
let nsec = usec.checked_mul(1000)?;
return Some((PayloadEncoding::Le64, Duration::new(sec, nsec)));
}
let sec = u64::from_be_bytes(payload[0..8].try_into().unwrap());
if sec.abs_diff(now.as_secs()) < 1000 && payload[8..12] == [0; 4] {
let usec = u32::from_be_bytes(payload[12..16].try_into().unwrap());
let nsec = usec.checked_mul(1000)?;
return Some((PayloadEncoding::Be64, Duration::new(sec, nsec)));
}
}
if payload.len() >= 8 {
let sec = u64::from(u32::from_le_bytes(payload[0..4].try_into().unwrap()));
if sec.abs_diff(now.as_secs()) < 1000 {
let usec = u32::from_le_bytes(payload[4..8].try_into().unwrap());
let nsec = usec.checked_mul(1000)?;
return Some((PayloadEncoding::Le32, Duration::new(sec, nsec)));
}
let sec = u64::from(u32::from_be_bytes(payload[0..4].try_into().unwrap()));
if sec.abs_diff(now.as_secs()) < 1000 {
let usec = u32::from_be_bytes(payload[4..8].try_into().unwrap());
let nsec = usec.checked_mul(1000)?;
return Some((PayloadEncoding::Be32, Duration::new(sec, nsec)));
}
}
None
}
fn write_timestamp_into_payload(
payload: &mut [u8],
encoding: PayloadEncoding,
timestamp: Duration,
) {
match encoding {
PayloadEncoding::Le64 => {
payload[0..8].copy_from_slice(×tamp.as_secs().to_le_bytes());
payload[8..16].copy_from_slice(&u64::from(timestamp.subsec_micros()).to_le_bytes());
}
PayloadEncoding::Be64 => {
payload[0..8].copy_from_slice(×tamp.as_secs().to_be_bytes());
payload[8..16].copy_from_slice(&u64::from(timestamp.subsec_micros()).to_be_bytes());
}
PayloadEncoding::Le32 => {
payload[0..4].copy_from_slice(&(timestamp.as_secs() as u32).to_le_bytes());
payload[4..8].copy_from_slice(×tamp.subsec_micros().to_le_bytes());
}
PayloadEncoding::Be32 => {
payload[0..4].copy_from_slice(&(timestamp.as_secs() as u32).to_be_bytes());
payload[4..8].copy_from_slice(×tamp.subsec_micros().to_be_bytes());
}
}
}
fn checksum(bytes: &[u8]) -> u16 {
!bytes
.chunks(2)
.map(|b| u16::from_be_bytes([b[0], if b.len() == 2 { b[1] } else { 0 }]))
.reduce(|a, b| {
let (sum, carry) = a.overflowing_add(b);
sum + carry as u16
})
.unwrap_or(0)
}
gitextract_kd58c96m/
├── .gitignore
├── Cargo.toml
├── README.md
└── src/
├── disable_system_pong.rs
└── main.rs
SYMBOL INDEX (13 symbols across 2 files)
FILE: src/disable_system_pong.rs
constant PATH_IPV4 (line 4) | const PATH_IPV4: &str = "/proc/sys/net/ipv4/icmp_echo_ignore_all";
constant PATH_IPV6 (line 5) | const PATH_IPV6: &str = "/proc/sys/net/ipv6/icmp/echo_ignore_all";
type DisableSystemPong (line 8) | pub struct DisableSystemPong {
method activate (line 14) | pub fn activate() -> Result<Self> {
method deactivate (line 53) | pub fn deactivate(&mut self) -> Result<()> {
method drop (line 71) | fn drop(&mut self) {
FILE: src/main.rs
type IcmpSocket (line 18) | type IcmpSocket = UdpSocket;
function open_icmp_socket (line 20) | fn open_icmp_socket(ipv6: bool) -> Result<IcmpSocket> {
function main (line 47) | fn main() -> Result<()> {
type PayloadEncoding (line 162) | enum PayloadEncoding {
function parse_payload (line 169) | fn parse_payload(now: Duration, payload: &[u8]) -> Option<(PayloadEncodi...
function write_timestamp_into_payload (line 201) | fn write_timestamp_into_payload(
function checksum (line 226) | fn checksum(bytes: &[u8]) -> u16 {
Condensed preview — 5 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (13K chars).
[
{
"path": ".gitignore",
"chars": 8,
"preview": "/target\n"
},
{
"path": "Cargo.toml",
"chars": 148,
"preview": "[package]\nname = \"pong\"\nversion = \"0.1.0\"\nedition = \"2021\"\n\n[dependencies]\nanyhow = \"1.0.52\"\nlibc = \"0.2.112\"\nnix = \"0.2"
},
{
"path": "README.md",
"chars": 654,
"preview": "# Pong\n\nA Linux program that replies to `ping` but modifies the payload of the ICMP\npacket to get lower ping times in so"
},
{
"path": "src/disable_system_pong.rs",
"chars": 2610,
"preview": "use anyhow::{bail, Context, Result};\nuse std::fs::{read, write};\n\nconst PATH_IPV4: &str = \"/proc/sys/net/ipv4/icmp_echo_"
},
{
"path": "src/main.rs",
"chars": 8679,
"preview": "#[cfg(not(target_os = \"linux\"))]\ncompile_error!(\"this program only works on Linux\");\n\nmod disable_system_pong;\n\nuse anyh"
}
]
About this extraction
This page contains the full source code of the m-ou-se/pong GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 5 files (11.8 KB), approximately 3.1k tokens, and a symbol index with 13 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.
Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.