Skip to content

Instantly share code, notes, and snippets.

@mnogu
Last active November 24, 2022 14:38
Show Gist options
  • Save mnogu/57b46c425469569abb9076438f311ce0 to your computer and use it in GitHub Desktop.
Save mnogu/57b46c425469569abb9076438f311ce0 to your computer and use it in GitHub Desktop.
ICMP test with a TUN interface tun0 on Ubuntu 22.04.1 LTS using Rust
$ sudo cargo run

Open another terminal window and send ICMP ECHO_REQUEST to 192.0.2.2, not 192.0.2.1:

$ ping 192.0.2.2
[package]
name = "pareiodon"
version = "0.1.0"
edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
nix = "0.25.0"
use std::{mem, os::raw::c_char};
use nix::{
fcntl::{open, OFlag},
ioctl_write_int, ioctl_write_ptr_bad, libc,
sys::{
ioctl::ioctl_param_type,
socket::{socket, AddressFamily, SockFlag, SockType, SockaddrIn},
stat::Mode,
},
unistd::{read, write},
};
ioctl_write_int!(tunsetiff, b'T', 202);
ioctl_write_ptr_bad!(siocsifaddr, libc::SIOCSIFADDR, libc::ifreq);
ioctl_write_ptr_bad!(siocsifnetmask, libc::SIOCSIFNETMASK, libc::ifreq);
ioctl_write_ptr_bad!(siocsifflags, libc::SIOCSIFFLAGS, libc::ifreq);
fn setup() -> i32 {
let fd = open("/dev/net/tun", OFlag::O_RDWR, Mode::empty()).unwrap();
let mut ifr_name: [c_char; libc::IF_NAMESIZE] = [0; libc::IF_NAMESIZE];
let name = b"tun0\0";
ifr_name[..name.len()].copy_from_slice(&name.map(|c| c as i8)[..]);
// Create a TUN interface tun0
let ifru_flags = (libc::IFF_TUN | libc::IFF_NO_PI) as i16;
let ifr_ifru = libc::__c_anonymous_ifr_ifru { ifru_flags };
let ifreq = libc::ifreq { ifr_name, ifr_ifru };
unsafe { tunsetiff(fd, &ifreq as *const libc::ifreq as ioctl_param_type) }.unwrap();
let sock = socket(
AddressFamily::Inet,
SockType::Datagram,
SockFlag::empty(),
None,
)
.unwrap();
// Assign 192.0.2.1 to tun0
let ifru_addr = SockaddrIn::new(192, 0, 2, 1, 0);
let ifru_addr = unsafe { mem::transmute(ifru_addr) };
let ifr_ifru = libc::__c_anonymous_ifr_ifru { ifru_addr };
let ifreq = libc::ifreq { ifr_name, ifr_ifru };
unsafe { siocsifaddr(sock, &ifreq) }.unwrap();
// Set the network mask for tun0 to 255.255.255.0 (/24)
let ifru_addr = SockaddrIn::new(255, 255, 255, 0, 0);
let ifru_addr = unsafe { mem::transmute(ifru_addr) };
let ifr_ifru = libc::__c_anonymous_ifr_ifru { ifru_addr };
let ifreq = libc::ifreq { ifr_name, ifr_ifru };
unsafe { siocsifnetmask(sock, &ifreq) }.unwrap();
// Make the state of tun0 up
let ifru_flags = libc::IFF_UP as i16;
let ifr_ifru = libc::__c_anonymous_ifr_ifru { ifru_flags };
let ifreq = libc::ifreq { ifr_name, ifr_ifru };
unsafe { siocsifflags(sock, &ifreq) }.unwrap();
fd
}
fn main() {
let fd = setup();
loop {
let mut buf = [0u8; 128];
read(fd, &mut buf).unwrap();
// Debug
println!("{:x?}", buf);
// Swap the source IP address for the destination IP address
for i in 12..16 {
buf.swap(i, i + 4);
}
// Type
// Echo Reply
buf[20] = 0;
// Set the checksum field to zero before computing a checksum
buf[22] = 0;
buf[23] = 0;
let mut checksum: u32 = 0;
for i in (20..buf.len()).step_by(2) {
checksum += ((buf[i] as u32) << 8) + buf[i + 1] as u32;
}
while (checksum >> 16) != 0 {
checksum = (checksum & 0xffff) + (checksum >> 16);
}
// one's complement
checksum = !checksum;
// Checksum
buf[22] = (checksum >> 8) as u8;
buf[23] = (checksum & 0xff) as u8;
write(fd, &buf).unwrap();
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment