I recently started to learn Rust and i'm trying out libpnet(github, crates). But after creating the code to build and Ethernet, IP and TCP header I stumbled upon an issue. It seems that adding tcp_header.set_options()
results in a panic.
thread 'main' panicked at 'called `Option::unwrap()` on a `None` value', /root/.cargo/registry/src/github.com-1ecc6299db9ec823/pnet_packet-0.30.0/src/tcp.rs:59:1
stack backtrace:
0: rust_begin_unwind
at /rustc/1.61.0/library/std/src/panicking.rs:584:5
1: core::panicking::panic_fmt
at /rustc/1.61.0/library/core/src/panicking.rs:143:14
2: core::panicking::panic
at /rustc/1.61.0/library/core/src/panicking.rs:48:5
3: core::option::Option<T>::unwrap
at /rustc/1.61.0/library/core/src/option.rs:755:21
4: pnet_packet::tcp::MutableTcpPacket::set_options
at /root/.cargo/registry/src/github.com-1ecc6299db9ec823/pnet_packet-0.30.0/src/tcp.rs:59:1
5: scanner::tcp::build_tcp_header
at ./src/tcp/mod.rs:86:5
6: scanner::tcp::new_packet
at ./src/tcp/mod.rs:26:22
7: scanner::main
at ./src/main.rs:32:22
8: core::ops::function::FnOnce::call_once
at /rustc/1.61.0/library/core/src/ops/function.rs:227:5
Running the code without adding the set_options() results the TCP packet being build and able to send. I'm not sure if this is the result of an error in my implementation or a bug in the library. I'm using pnet = "0.30.0"
./main.rs
use pnet::datalink::interfaces;
use pnet::datalink::Channel;
use pnet::packet::Packet;
use std::net::Ipv4Addr;
use std::str::FromStr;
mod tcp;
fn main() {
// get interfaces
let all_interfaces = interfaces();
let interfaces = all_interfaces
.iter()
.find(|e| e.is_up() && !e.is_loopback() && !e.ips.is_empty());
match interfaces {
Some(interface) => println!(
"Interface name: {}\nInterface MAC : {}\nInterface IP : {}\n",
interface.name,
interface.mac.unwrap(),
interface.ips[0]
),
None => println!("Error while finding the default interface."),
}
let if_name = &interfaces.unwrap().name;
let if_mac = interfaces.unwrap().mac.unwrap();
let if_ip = interfaces.unwrap().ips[0];
let dst_ip = Ipv4Addr::from_str("10.0.0.2").unwrap();
let src_ip = Ipv4Addr::from_str("10.0.0.90").unwrap();
let new_packet = tcp::new_packet(&dst_ip, &src_ip, &if_mac);
let (mut sender, mut receiver) =
match pnet::datalink::channel(&interfaces.unwrap(), Default::default()) {
Ok(Channel::Ethernet(tx, rx)) => (tx, rx),
Ok(_) => panic!("Unknown channel type"),
Err(e) => panic!("Error happened {}", e),
};
let ethernet_packet = new_packet.packet();
sender.send_to(ethernet_packet, None).unwrap().unwrap();
if let Ok(packet) = receiver.next() {
// match packet.get_flags() {
// 18 => println!("open"),
// _ => println!("closed"),
// }
}
}
./tcp/mod.rs
use pnet::packet::ethernet::EtherTypes;
use pnet::packet::ethernet::EthernetPacket;
use pnet::packet::ethernet::MutableEthernetPacket;
use pnet::packet::ip::IpNextHeaderProtocols;
use pnet::packet::ipv4::checksum;
use pnet::packet::ipv4::Ipv4Flags;
use pnet::packet::ipv4::MutableIpv4Packet;
use pnet::packet::tcp::ipv4_checksum;
use pnet::packet::tcp::MutableTcpPacket;
use pnet::packet::tcp::TcpFlags;
use pnet::packet::tcp::TcpOption;
use pnet::packet::Packet;
use pnet::util::MacAddr;
use rand::Rng;
use std::net::Ipv4Addr;
const ETHERNET_HEADER_LEN: usize = 14;
const IPV4_HEADER_LEN: usize = 24;
const TCP_HEADER_LEN: usize = 20;
pub fn new_packet<'a>(
dst_ip: &'a Ipv4Addr,
src_ip: &'a Ipv4Addr,
if_mac: &'a MacAddr,
) -> EthernetPacket<'a> {
let tcp_header = build_tcp_header(8006, src_ip, dst_ip);
let ipv4_header = build_ipv4_header(tcp_header, src_ip, dst_ip);
let packet = build_ethernet_header(ipv4_header, if_mac);
packet.consume_to_immutable()
}
fn build_ethernet_header<'a>(
payload: MutableIpv4Packet,
if_mac: &'a MacAddr,
) -> MutableEthernetPacket<'a> {
let buff: Vec<u8> = vec![0; ETHERNET_HEADER_LEN + IPV4_HEADER_LEN + TCP_HEADER_LEN];
let mut ethernet_header = MutableEthernetPacket::owned(buff).unwrap();
ethernet_header.set_destination(MacAddr::broadcast());
ethernet_header.set_source(*if_mac);
ethernet_header.set_ethertype(EtherTypes::Ipv4);
ethernet_header.set_payload(payload.to_immutable().packet()); // Concat protocol payload with ip header
println!("{}", format!("{ethernet_header:?}"));
ethernet_header
}
fn build_ipv4_header<'a>(
payload: MutableTcpPacket,
src_ip: &'a Ipv4Addr,
dst_ip: &'a Ipv4Addr,
) -> MutableIpv4Packet<'a> {
let buff: Vec<u8> = vec![0; IPV4_HEADER_LEN + TCP_HEADER_LEN];
let mut ipv4_header = MutableIpv4Packet::owned(buff).unwrap();
ipv4_header.set_version(4); // IP header Version
ipv4_header.set_header_length(6); // IP header Lenght
ipv4_header.set_dscp(0); // IP header Differentiated Services Code Point
ipv4_header.set_ecn(0); // IP header Explicit Congestion Notification
ipv4_header.set_total_length(u16::try_from(IPV4_HEADER_LEN + TCP_HEADER_LEN).unwrap()); // IP header total length
ipv4_header.set_flags(Ipv4Flags::DontFragment); // IP header Flags
ipv4_header.set_fragment_offset(0); // IP header Fragmentation offset
ipv4_header.set_ttl(255); // IP header Time To Live
ipv4_header.set_next_level_protocol(IpNextHeaderProtocols::Tcp); // IP header Protocol
ipv4_header.set_source(*src_ip); // IP header Source IP address
ipv4_header.set_destination(*dst_ip); // IP header Destination IP address
ipv4_header.set_checksum(checksum(&ipv4_header.to_immutable())); // IP header Checksum
println!("{}", format!("{ipv4_header:?}"));
ipv4_header.set_payload(payload.to_immutable().packet()); // Concat protocol payload with tcp header
ipv4_header
}
fn build_tcp_header<'a>(
dest: u16,
src_ip: &'a Ipv4Addr,
dst_ip: &'a Ipv4Addr,
) -> MutableTcpPacket<'a> {
let buff: Vec<u8> = vec![0; TCP_HEADER_LEN];
let mut tcp_header = MutableTcpPacket::owned(buff).unwrap();
tcp_header.set_source(12345); // TCP header source port
tcp_header.set_destination(dest); // TCP header destination port
tcp_header.set_sequence(rand::thread_rng().gen::<u32>()); // TCP header sequence number
tcp_header.set_acknowledgement(0); // TCP header acknowledgement number
tcp_header.set_data_offset(8); // TCP header data offset
tcp_header.set_reserved(0); // TCP header reserved
tcp_header.set_flags(TcpFlags::SYN); // TCP header flags
tcp_header.set_window(64240); // TCP header window size
tcp_header.set_urgent_ptr(0); // TCP header urgent
tcp_header.set_options(&[ // <----- panic <----- panic <----- panic
TcpOption::mss(1460),
TcpOption::sack_perm(),
TcpOption::nop(),
TcpOption::nop(),
TcpOption::wscale(7),
]);
tcp_header.set_checksum(ipv4_checksum(&tcp_header.to_immutable(), src_ip, dst_ip));
println!("{}", format!("{tcp_header:?}"));
tcp_header
}