Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Constructing multi-layer packets #580

Open
mikebromwich opened this issue Aug 21, 2022 · 0 comments
Open

Constructing multi-layer packets #580

mikebromwich opened this issue Aug 21, 2022 · 0 comments

Comments

@mikebromwich
Copy link

I'm struggling to understand the principles of how to correctly create multi-layer packets (such as ICMP->IPv4->Ethernet) with libpnet - in particular, how to correctly define packet/buffer/payload sizes between the layers. I seem to be getting inconsistent behaviour between the various layers - and so I am probably misunderstanding how this is supposed to work.

I am working from the 'outside-in' so (for example) creating an ICMP message, then wrapping that in IPv4, then wrapping that in Ethernet. This is already requiring me to make three allocations/copies - but more fundamentally, I can't see how to manage the payload extents consistently.

My example code is below.

The EchoReplyPacket.packet_size() reports 8 - so this is just the header (excludes the payload). EchoReplyPacket.payload().len() reports 32. (total 40 bytes).

Ipv4Packet.packet_size() reports 60 - so this includes both the header (20 bytes) and the ICMP payload (40) (total 60 bytes). Ipv4Packet.payload().size reports 40.

Ethernet.packet_size() reports 14 - so this is just the header. Ethernet.payload().len() reports 60. (total 74 bytes).

This seems inconsistent. Why does the IPv4 packet_size include the payload - but Ethernet and ICMP do not?

Also, even though I have created the Ipv4Packet with the appropriately sized buffer, I also have to call set_total_length before I can set the payload (otherwise panic) - is this correct? I don't seem to have to do this for Ethernet or ICMP layers.

Ideally, I would just create a single generously-sized buffer at the outset, and the write Ethernet, IPv4, ICMP layers in sequence - then patch-up the lengths and checksums. Then, send the appropriate slice of this buffer to the network. If anyone has an examples of this approach, it would be helpful/appreciated.

Thanks,

let echo_request_packet = EchoRequestPacket::new(received_ipv4.payload()).unwrap();

// Construct ICMP Echo Reply
let mut echo_reply_buffer = vec![0u8;echo_request_packet.packet_size()+echo_request_packet.payload().len()];
let mut echo_reply_packet = pnet::packet::icmp::echo_reply::MutableEchoReplyPacket::new(&mut echo_reply_buffer).unwrap();

echo_reply_packet.set_identifier(echo_request_packet.get_identifier());
echo_reply_packet.set_sequence_number(echo_request_packet.get_sequence_number());
echo_reply_packet.set_payload(echo_request_packet.payload());
echo_reply_packet.set_checksum(pnet::util::checksum(echo_reply_packet.packet(),1));

println!("ICMP echo reply size {}",echo_reply_packet.packet_size());
println!("ICMP echo reply payload size {}",echo_reply_packet.payload().len());

// Construct IPv4 Packet
let ipv4_len = echo_request_packet.packet_size()+echo_request_packet.payload().len()+20;
let mut ipv4_buffer = vec![0u8; ipv4_len];
let mut ipv4_packet = MutableIpv4Packet::new(&mut ipv4_buffer).unwrap();

ipv4_packet.set_version(4);
ipv4_packet.set_header_length(5);
ipv4_packet.set_dscp(4);
ipv4_packet.set_ecn(1);
ipv4_packet.set_ttl(64);
ipv4_packet.set_next_level_protocol(IpNextHeaderProtocols::Icmp);
ipv4_packet.set_source(received_ipv4.get_destination());
ipv4_packet.set_destination(received_ipv4.get_source());
ipv4_packet.set_total_length(ipv4_len.try_into().unwrap());
ipv4_packet.set_payload(echo_reply_packet.packet_mut());
ipv4_packet.set_checksum(pnet::packet::ipv4::checksum(&ipv4_packet.to_immutable()));
println!("IPv4 size {}",ipv4_packet.packet_size());
println!("IPv4 payload size {}",ipv4_packet.payload().len());

// Construct Ethernet frame
let ethernet_len = ipv4_packet.packet_size()+14;
let mut ethernet_buffer = vec![0u8; ethernet_len];
let mut ethernet_packet = MutableEthernetPacket::new(&mut ethernet_buffer).unwrap();
                
ethernet_packet.set_destination(ethernet.get_source());
ethernet_packet.set_source(ethernet.get_destination());
ethernet_packet.set_ethertype(EtherTypes::Ipv4);
ethernet_packet.set_payload(ipv4_packet.packet_mut());
println!("Eth size {}",ethernet_packet.packet_size());
println!("Eth payload size {}",ethernet_packet.payload().len());


Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant