refactor(connlib): merge IpPacket and MutableIpPacket (#6652)

Currently, we have two structs for representing IP packets: `IpPacket`
and `MutableIpPacket`. As the name suggests, they mostly differ in
mutability. This design was originally inspired by the `pnet_packet`
crate which we based our `IpPacket` on. With subsequent iterations, we
added more and more functionality onto our `IpPacket`, like NAT64 &
NAT46 translation. As a result of that, the `MutableIpPacket` is no
longer directly based on `pnet_packet` but instead just keeps an
internal buffer.

This duplication can be resolved by merging the two structs into a
single `IpPacket`. We do this by first replacing all usages of
`IpPacket` with `MutableIpPacket`, deleting `IpPacket` and renaming
`MutableIpPacket` to `IpPacket`. The final design now has different
`self`-receivers: Some functions take `&self`, some `&mut self` and some
consume the packet using `self`.

This results in a more ergonomic usage of `IpPacket` across the codebase
and deletes a fair bit of code. It also takes us one step closer towards
using `etherparse` for all our IP packet interaction-needs. Lastly, I am
currently exploring a performance-optimisation idea that stack-allocates
all IP packets and for that, the current split between `IpPacket` and
`MutableIpPacket` does not really work.

Related: #6366.
This commit is contained in:
Thomas Eizinger
2024-09-11 18:32:49 -04:00
committed by GitHub
parent 828a70e229
commit 133c2565b2
19 changed files with 386 additions and 624 deletions

View File

@@ -58,8 +58,8 @@ jobs:
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- run: |
rustup install --no-self-update nightly-2024-06-01 --profile minimal # The exact nightly version doesn't matter, just pin a random one.
cargo +nightly-2024-06-01 udeps --all-targets --all-features ${{ steps.setup-rust.outputs.packages }}
rustup install --no-self-update nightly-2024-09-01 --profile minimal # The exact nightly version doesn't matter, just pin a random one.
cargo +nightly-2024-09-01 udeps --all-targets --all-features ${{ steps.setup-rust.outputs.packages }}
name: Check for unused dependencies
- run: cargo fmt -- --check
- run: cargo doc --all-features --no-deps --document-private-items ${{ steps.setup-rust.outputs.packages }}

View File

@@ -63,14 +63,14 @@ mod platform {
let mut response_pkt = None;
let mut time_spent = Duration::from_millis(0);
loop {
let mut req_buf = [0u8; MTU];
poll_fn(|cx| tun.poll_read(&mut req_buf, cx)).await?;
let mut req_buf = [0u8; MTU + 20];
poll_fn(|cx| tun.poll_read(&mut req_buf[20..], cx)).await?;
let start = Instant::now();
let original_pkt = IpPacket::new(&req_buf).unwrap();
let original_pkt = IpPacket::new(&mut req_buf).unwrap();
let Some(original_udp) = original_pkt.as_udp() else {
continue;
};
if original_udp.get_destination() != SERVER_PORT {
if original_udp.destination_port() != SERVER_PORT {
continue;
}
if original_udp.payload()[0] != REQ_CODE {
@@ -84,8 +84,8 @@ mod platform {
ip_packet::make::udp_packet(
original_pkt.destination(),
original_pkt.source(),
original_udp.get_destination(),
original_udp.get_source(),
original_udp.destination_port(),
original_udp.source_port(),
vec![RESP_CODE],
)
.unwrap()

View File

@@ -9,9 +9,7 @@ use boringtun::x25519::PublicKey;
use boringtun::{noise::rate_limiter::RateLimiter, x25519::StaticSecret};
use core::fmt;
use hex_display::HexDisplayExt;
use ip_packet::{
ConvertibleIpv4Packet, ConvertibleIpv6Packet, IpPacket, MutableIpPacket, Packet as _,
};
use ip_packet::{ConvertibleIpv4Packet, ConvertibleIpv6Packet, IpPacket, Packet as _};
use rand::rngs::StdRng;
use rand::seq::IteratorRandom;
use rand::{random, SeedableRng};
@@ -294,7 +292,7 @@ where
packet: &[u8],
now: Instant,
buffer: &'b mut [u8],
) -> Result<Option<(TId, MutableIpPacket<'b>)>, Error> {
) -> Result<Option<(TId, IpPacket<'b>)>, Error> {
self.add_local_as_host_candidate(local)?;
let (from, packet, relayed) = match self.allocations_try_handle(from, local, packet, now) {
@@ -716,7 +714,7 @@ where
packet: &[u8],
buffer: &'b mut [u8],
now: Instant,
) -> ControlFlow<Result<(), Error>, (TId, MutableIpPacket<'b>)> {
) -> ControlFlow<Result<(), Error>, (TId, IpPacket<'b>)> {
for (cid, conn) in self.connections.iter_established_mut() {
if !conn.accepts(&from) {
continue;
@@ -1713,7 +1711,7 @@ where
allocations: &mut BTreeMap<RId, Allocation>,
transmits: &mut VecDeque<Transmit<'static>>,
now: Instant,
) -> ControlFlow<Result<(), Error>, MutableIpPacket<'b>> {
) -> ControlFlow<Result<(), Error>, IpPacket<'b>> {
let _guard = self.span.enter();
let control_flow = match self.tunnel.decapsulate(None, packet, &mut buffer[20..]) {

View File

@@ -13,7 +13,7 @@ use connlib_shared::messages::{
use connlib_shared::{callbacks, PublicKey, StaticSecret};
use ip_network::{IpNetwork, Ipv4Network, Ipv6Network};
use ip_network_table::IpNetworkTable;
use ip_packet::{IpPacket, MutableIpPacket, Packet as _};
use ip_packet::IpPacket;
use itertools::Itertools;
use crate::peer::GatewayOnClient;
@@ -390,7 +390,7 @@ impl ClientState {
pub(crate) fn encapsulate(
&mut self,
packet: MutableIpPacket<'_>,
packet: IpPacket<'_>,
now: Instant,
buffer: &mut EncryptBuffer,
) -> Option<snownet::EncryptedPacket> {
@@ -439,7 +439,7 @@ impl ClientState {
let transmit = self
.node
.encapsulate(gid, packet.as_immutable(), now, buffer)
.encapsulate(gid, packet, now, buffer)
.inspect_err(|e| tracing::debug!(%gid, "Failed to encapsulate: {e}"))
.ok()??;
@@ -485,7 +485,7 @@ impl ClientState {
now,
);
Some(packet.into_immutable())
Some(packet)
}
pub fn add_ice_candidate(&mut self, conn_id: GatewayId, ice_candidate: String, now: Instant) {
@@ -618,13 +618,10 @@ impl ClientState {
/// Returns `Err` if the packet is not a DNS query.
fn try_handle_dns_query<'a>(
&mut self,
packet: MutableIpPacket<'a>,
packet: IpPacket<'a>,
now: Instant,
) -> Result<Option<IpPacket<'static>>, (MutableIpPacket<'a>, IpAddr)> {
match self
.stub_resolver
.handle(&self.dns_mapping, packet.as_immutable())
{
) -> Result<Option<IpPacket<'static>>, (IpPacket<'a>, IpAddr)> {
match self.stub_resolver.handle(&self.dns_mapping, &packet) {
Some(dns::ResolveStrategy::LocalResponse(query)) => Ok(Some(query)),
Some(dns::ResolveStrategy::ForwardQuery {
upstream: server,
@@ -680,7 +677,7 @@ impl ClientState {
.inspect_err(|_| tracing::warn!("Failed to find original dst for DNS response"))
.ok()?;
Some(ip_packet.into_immutable())
Some(ip_packet)
}
pub fn on_connection_failed(&mut self, resource: ResourceId) {
@@ -1327,11 +1324,11 @@ fn is_definitely_not_a_resource(ip: IpAddr) -> bool {
/// In case the given packet is a DNS query, change its source IP to that of the actual DNS server.
fn maybe_mangle_dns_query_to_cidr_resource<'p>(
mut packet: MutableIpPacket<'p>,
mut packet: IpPacket<'p>,
dns_mapping: &BiMap<IpAddr, DnsServer>,
mangeled_dns_queries: &mut HashMap<u16, Instant>,
now: Instant,
) -> MutableIpPacket<'p> {
) -> IpPacket<'p> {
let dst = packet.destination();
let Some(srv) = dns_mapping.get_by_left(&dst) else {
@@ -1356,18 +1353,18 @@ fn maybe_mangle_dns_query_to_cidr_resource<'p>(
}
fn maybe_mangle_dns_response_from_cidr_resource<'p>(
mut packet: MutableIpPacket<'p>,
mut packet: IpPacket<'p>,
dns_mapping: &BiMap<IpAddr, DnsServer>,
mangeled_dns_queries: &mut HashMap<u16, Instant>,
now: Instant,
) -> MutableIpPacket<'p> {
) -> IpPacket<'p> {
let src_ip = packet.source();
let Some(udp) = packet.as_udp() else {
return packet;
};
let src_port = udp.get_source();
let src_port = udp.source_port();
let Some(sentinel) = dns_mapping.get_by_right(&DnsServer::from((src_ip, src_port))) else {
return packet;

View File

@@ -1,4 +1,4 @@
use ip_packet::{IpPacket, MutableIpPacket, Packet as _};
use ip_packet::{IpPacket, Packet as _};
use std::io;
use std::task::{Context, Poll, Waker};
use tun::Tun;
@@ -30,7 +30,7 @@ impl Device {
&mut self,
buf: &'b mut [u8],
cx: &mut Context<'_>,
) -> Poll<io::Result<MutableIpPacket<'b>>> {
) -> Poll<io::Result<IpPacket<'b>>> {
use ip_packet::Packet as _;
let Some(tun) = self.tun.as_mut() else {
@@ -47,7 +47,7 @@ impl Device {
)));
}
let packet = MutableIpPacket::new(&mut buf[..(n + 20)]).ok_or_else(|| {
let packet = IpPacket::new(&mut buf[..(n + 20)]).ok_or_else(|| {
io::Error::new(
io::ErrorKind::InvalidInput,
"received bytes are not an IP packet",

View File

@@ -7,7 +7,6 @@ use domain::base::{
};
use domain::rdata::AllRecordData;
use ip_packet::IpPacket;
use ip_packet::Packet as _;
use itertools::Itertools;
use pattern::{Candidate, Pattern};
use std::collections::{BTreeMap, HashMap};
@@ -209,13 +208,13 @@ impl StubResolver {
pub(crate) fn handle(
&mut self,
dns_mapping: &bimap::BiMap<IpAddr, DnsServer>,
packet: IpPacket,
packet: &IpPacket,
) -> Option<ResolveStrategy> {
let upstream = dns_mapping.get_by_left(&packet.destination())?.address();
let datagram = packet.as_udp()?;
// We only support DNS on port 53.
if datagram.get_destination() != DNS_PORT {
if datagram.destination_port() != DNS_PORT {
return None;
}
@@ -241,12 +240,11 @@ impl StubResolver {
let packet = ip_packet::make::udp_packet(
packet.destination(),
packet.source(),
datagram.get_destination(),
datagram.get_source(),
datagram.destination_port(),
datagram.source_port(),
response,
)
.expect("src and dst come from the same packet")
.into_immutable();
.expect("src and dst come from the same packet");
return Some(ResolveStrategy::LocalResponse(packet));
}
@@ -260,7 +258,7 @@ impl StubResolver {
upstream,
query_id: message.header().id(),
payload: message.into_octets().to_vec(),
original_src: SocketAddr::new(packet.source(), datagram.get_source()),
original_src: SocketAddr::new(packet.source(), datagram.source_port()),
})
}
(Rtype::A, Some(resource)) => self.get_or_assign_a_records(domain.clone(), resource),
@@ -277,7 +275,7 @@ impl StubResolver {
upstream,
query_id: message.header().id(),
payload: message.into_octets().to_vec(),
original_src: SocketAddr::new(packet.source(), datagram.get_source()),
original_src: SocketAddr::new(packet.source(), datagram.source_port()),
})
}
};
@@ -286,12 +284,11 @@ impl StubResolver {
let packet = ip_packet::make::udp_packet(
packet.destination(),
packet.source(),
datagram.get_destination(),
datagram.get_source(),
datagram.destination_port(),
datagram.source_port(),
response,
)
.expect("src and dst come from the same packet")
.into_immutable();
.expect("src and dst come from the same packet");
Some(ResolveStrategy::LocalResponse(packet))
}

View File

@@ -11,7 +11,7 @@ use connlib_shared::messages::{
};
use connlib_shared::{DomainName, StaticSecret};
use ip_network::{Ipv4Network, Ipv6Network};
use ip_packet::{IpPacket, MutableIpPacket};
use ip_packet::IpPacket;
use secrecy::{ExposeSecret as _, Secret};
use snownet::{EncryptBuffer, RelaySocket, ServerNode};
use std::collections::{BTreeMap, BTreeSet, VecDeque};
@@ -157,7 +157,7 @@ impl GatewayState {
pub(crate) fn encapsulate(
&mut self,
packet: MutableIpPacket<'_>,
packet: IpPacket<'_>,
now: Instant,
buffer: &mut EncryptBuffer,
) -> Option<snownet::EncryptedPacket> {
@@ -181,7 +181,7 @@ impl GatewayState {
let transmit = self
.node
.encapsulate(peer.id(), packet.as_immutable(), now, buffer)
.encapsulate(peer.id(), packet, now, buffer)
.inspect_err(|e| tracing::debug!(%cid, "Failed to encapsulate: {e}"))
.ok()??;
@@ -217,7 +217,7 @@ impl GatewayState {
.inspect_err(|e| tracing::debug!(%cid, "Invalid packet: {e:#}"))
.ok()?;
Some(packet.into_immutable())
Some(packet)
}
pub fn add_ice_candidate(&mut self, conn_id: ClientId, ice_candidate: String, now: Instant) {

View File

@@ -1,6 +1,6 @@
use crate::{device_channel::Device, sockets::Sockets, BUF_SIZE};
use futures_util::FutureExt as _;
use ip_packet::{IpPacket, MutableIpPacket};
use ip_packet::IpPacket;
use snownet::{EncryptBuffer, EncryptedPacket};
use socket_factory::{DatagramIn, DatagramOut, SocketFactory, TcpSocket, UdpSocket};
use std::{
@@ -29,7 +29,7 @@ pub struct Io {
pub enum Input<'a, I> {
Timeout(Instant),
Device(MutableIpPacket<'a>),
Device(IpPacket<'a>),
Network(I),
}

View File

@@ -10,8 +10,7 @@ use connlib_shared::messages::{
use connlib_shared::DomainName;
use ip_network::IpNetwork;
use ip_network_table::IpNetworkTable;
use ip_packet::ip::IpNextHeaderProtocols;
use ip_packet::{IpPacket, MutableIpPacket};
use ip_packet::IpPacket;
use itertools::Itertools;
use rangemap::RangeInclusiveSet;
@@ -70,20 +69,19 @@ impl AllowRules {
}
fn is_allowed(&self, packet: &IpPacket) -> bool {
match packet.next_header() {
// Note: possible optimization here
// if we want to get the port here, and we assume correct formatting
// we can do packet.payload()[2..=3] (for UDP and TCP bytes 2 and 3 are the port)
// but it might be a bit harder to read
IpNextHeaderProtocols::Tcp => packet
.as_tcp()
.is_some_and(|p| self.tcp.contains(&p.get_destination())),
IpNextHeaderProtocols::Udp => packet
.as_udp()
.is_some_and(|p| self.udp.contains(&p.get_destination())),
IpNextHeaderProtocols::Icmp | IpNextHeaderProtocols::Icmpv6 => self.icmp,
_ => false,
if let Some(tcp) = packet.as_tcp() {
return self.tcp.contains(&tcp.destination_port());
}
if let Some(udp) = packet.as_udp() {
return self.udp.contains(&udp.destination_port());
}
if packet.is_icmp_v4_or_v6() {
return self.icmp;
}
false
}
fn add_filters<'a>(&mut self, filters: impl IntoIterator<Item = &'a Filter>) {
@@ -377,16 +375,16 @@ impl ClientOnGateway {
fn transform_network_to_tun<'a>(
&mut self,
packet: MutableIpPacket<'a>,
packet: IpPacket<'a>,
now: Instant,
) -> anyhow::Result<MutableIpPacket<'a>> {
) -> anyhow::Result<IpPacket<'a>> {
let Some(state) = self.permanent_translations.get_mut(&packet.destination()) else {
return Ok(packet);
};
let (source_protocol, real_ip) =
self.nat_table
.translate_outgoing(packet.as_immutable(), state.resolved_ip, now)?;
.translate_outgoing(&packet, state.resolved_ip, now)?;
let mut packet = packet
.translate_destination(self.ipv4, self.ipv6, source_protocol, real_ip)
@@ -400,9 +398,9 @@ impl ClientOnGateway {
pub fn decapsulate<'a>(
&mut self,
packet: MutableIpPacket<'a>,
packet: IpPacket<'a>,
now: Instant,
) -> anyhow::Result<MutableIpPacket<'a>> {
) -> anyhow::Result<IpPacket<'a>> {
self.ensure_allowed_src(&packet)?;
let packet = self.transform_network_to_tun(packet, now)?;
@@ -414,13 +412,10 @@ impl ClientOnGateway {
pub fn encapsulate<'a>(
&mut self,
packet: MutableIpPacket<'a>,
packet: IpPacket<'a>,
now: Instant,
) -> anyhow::Result<Option<MutableIpPacket<'a>>> {
let Some((proto, ip)) = self
.nat_table
.translate_incoming(packet.as_immutable(), now)?
else {
) -> anyhow::Result<Option<IpPacket<'a>>> {
let Some((proto, ip)) = self.nat_table.translate_incoming(&packet, now)? else {
return Ok(Some(packet));
};
@@ -438,7 +433,7 @@ impl ClientOnGateway {
Ok(Some(packet))
}
fn ensure_allowed_src(&self, packet: &MutableIpPacket<'_>) -> anyhow::Result<()> {
fn ensure_allowed_src(&self, packet: &IpPacket<'_>) -> anyhow::Result<()> {
let src = packet.source();
if !self.allowed_ips().contains(&src) {
@@ -449,12 +444,12 @@ impl ClientOnGateway {
}
/// Check if an incoming packet arriving over the network is ok to be forwarded to the TUN device.
fn ensure_allowed_dst(&self, packet: &MutableIpPacket<'_>) -> anyhow::Result<()> {
fn ensure_allowed_dst(&mut self, packet: &IpPacket<'_>) -> anyhow::Result<()> {
let dst = packet.destination();
if !self
.filters
.longest_match(dst)
.is_some_and(|(_, filter)| filter.is_allowed(&packet.to_immutable()))
.is_some_and(|(_, filter)| filter.is_allowed(packet))
{
return Err(anyhow::Error::new(DstNotAllowed(dst)));
};
@@ -468,7 +463,7 @@ impl ClientOnGateway {
}
impl GatewayOnClient {
pub(crate) fn ensure_allowed_src(&self, packet: &MutableIpPacket) -> anyhow::Result<()> {
pub(crate) fn ensure_allowed_src(&self, packet: &IpPacket) -> anyhow::Result<()> {
let src = packet.source();
if self.allowed_ips.longest_match(src).is_none() {

View File

@@ -44,7 +44,7 @@ impl NatTable {
pub(crate) fn translate_outgoing(
&mut self,
packet: IpPacket,
packet: &IpPacket,
outside_dst: IpAddr,
now: Instant,
) -> anyhow::Result<(Protocol, IpAddr)> {
@@ -84,7 +84,7 @@ impl NatTable {
pub(crate) fn translate_incoming(
&mut self,
packet: IpPacket,
packet: &IpPacket,
now: Instant,
) -> anyhow::Result<Option<(Protocol, IpAddr)>> {
let outside = (packet.destination_protocol()?, packet.source());
@@ -105,12 +105,12 @@ impl NatTable {
#[cfg(all(test, feature = "proptest"))]
mod tests {
use super::*;
use ip_packet::{proptest::*, MutableIpPacket};
use ip_packet::{proptest::*, IpPacket};
use proptest::prelude::*;
#[test_strategy::proptest(ProptestConfig { max_local_rejects: 10_000, max_global_rejects: 10_000, ..ProptestConfig::default() })]
fn translates_back_and_forth_packet(
#[strategy(udp_or_tcp_or_icmp_packet())] packet: MutableIpPacket<'static>,
#[strategy(udp_or_tcp_or_icmp_packet())] packet: IpPacket<'static>,
#[strategy(any::<IpAddr>())] outside_dst: IpAddr,
#[strategy(0..120u64)] response_delay: u64,
) {
@@ -121,12 +121,12 @@ mod tests {
let response_delay = Duration::from_secs(response_delay);
// Remember original src_p and dst
let src = packet.as_immutable().source_protocol().unwrap();
let src = packet.source_protocol().unwrap();
let dst = packet.destination();
// Translate out
let (new_source_protocol, new_dst_ip) = table
.translate_outgoing(packet.as_immutable(), outside_dst, sent_at)
.translate_outgoing(&packet, outside_dst, sent_at)
.unwrap();
// Pretend we are getting a response.
@@ -139,7 +139,7 @@ mod tests {
// Translate in
let translate_incoming = table
.translate_incoming(response.as_immutable(), sent_at + response_delay)
.translate_incoming(&response, sent_at + response_delay)
.unwrap();
// Assert
@@ -152,16 +152,15 @@ mod tests {
#[test_strategy::proptest(ProptestConfig { max_local_rejects: 10_000, max_global_rejects: 10_000, ..ProptestConfig::default() })]
fn can_handle_multiple_packets(
#[strategy(udp_or_tcp_or_icmp_packet())] packet1: MutableIpPacket<'static>,
#[strategy(udp_or_tcp_or_icmp_packet())] packet1: IpPacket<'static>,
#[strategy(any::<IpAddr>())] outside_dst1: IpAddr,
#[strategy(udp_or_tcp_or_icmp_packet())] packet2: MutableIpPacket<'static>,
#[strategy(udp_or_tcp_or_icmp_packet())] packet2: IpPacket<'static>,
#[strategy(any::<IpAddr>())] outside_dst2: IpAddr,
) {
proptest::prop_assume!(packet1.destination().is_ipv4() == outside_dst1.is_ipv4()); // Required for our test to simulate a response.
proptest::prop_assume!(packet2.destination().is_ipv4() == outside_dst2.is_ipv4()); // Required for our test to simulate a response.
proptest::prop_assume!(
packet1.as_immutable().source_protocol().unwrap()
!= packet2.as_immutable().source_protocol().unwrap()
packet1.source_protocol().unwrap() != packet2.source_protocol().unwrap()
);
let mut table = NatTable::default();
@@ -171,14 +170,12 @@ mod tests {
// Remember original src_p and dst
let original_src_p_and_dst = packets
.clone()
.map(|(p, _)| (p.as_immutable().source_protocol().unwrap(), p.destination()));
.map(|(p, _)| (p.source_protocol().unwrap(), p.destination()));
// Translate out
let new_src_p_and_dst = packets.clone().map(|(p, d)| {
table
.translate_outgoing(p.as_immutable(), d, Instant::now())
.unwrap()
});
let new_src_p_and_dst = packets
.clone()
.map(|(p, d)| table.translate_outgoing(&p, d, Instant::now()).unwrap());
// Pretend we are getting a response.
for ((p, _), (new_src_p, new_d)) in packets.iter_mut().zip(new_src_p_and_dst) {
@@ -189,7 +186,7 @@ mod tests {
// Translate in
let responses = packets.map(|(p, _)| {
table
.translate_incoming(p.as_immutable(), Instant::now())
.translate_incoming(&p, Instant::now())
.unwrap()
.unwrap()
});

View File

@@ -224,11 +224,11 @@ fn assert_correct_src_and_dst_udp_ports(
client_sent_request: &IpPacket<'_>,
client_received_reply: &IpPacket<'_>,
) {
let client_sent_request = client_sent_request.unwrap_as_udp();
let client_received_reply = client_received_reply.unwrap_as_udp();
let client_sent_request = client_sent_request.as_udp().unwrap();
let client_received_reply = client_received_reply.as_udp().unwrap();
let req_dst = client_sent_request.get_destination();
let res_src = client_received_reply.get_source();
let req_dst = client_sent_request.destination_port();
let res_src = client_received_reply.source_port();
if req_dst != res_src {
tracing::error!(target: "assertions", %req_dst, %res_src, "❌ req dst port != res src port");
@@ -236,8 +236,8 @@ fn assert_correct_src_and_dst_udp_ports(
tracing::info!(target: "assertions", port = %req_dst, "✅ req dst port == res src port");
}
let req_src = client_sent_request.get_source();
let res_dst = client_received_reply.get_destination();
let req_src = client_sent_request.source_port();
let res_dst = client_received_reply.destination_port();
if req_src != res_dst {
tracing::error!(target: "assertions", %req_src, %res_dst, "❌ req src port != res dst port");

View File

@@ -24,7 +24,7 @@ use domain::{
};
use ip_network::{IpNetwork, Ipv4Network, Ipv6Network};
use ip_network_table::IpNetworkTable;
use ip_packet::{IpPacket, MutableIpPacket, Packet as _};
use ip_packet::IpPacket;
use itertools::Itertools as _;
use prop::collection;
use proptest::prelude::*;
@@ -120,23 +120,19 @@ impl SimClient {
pub(crate) fn encapsulate(
&mut self,
packet: MutableIpPacket<'static>,
packet: IpPacket<'static>,
now: Instant,
) -> Option<snownet::Transmit<'static>> {
{
let packet = packet.as_immutable().to_owned();
if let Some(icmp) = packet.as_icmp() {
let echo_request = icmp.as_echo_request().expect("to be echo request");
let echo_request = icmp.echo_request_header().expect("to be echo request");
self.sent_icmp_requests
.insert((echo_request.sequence(), echo_request.identifier()), packet);
.insert((echo_request.seq, echo_request.id), packet.clone());
}
}
{
let packet = packet.as_immutable().to_owned();
if let Some(udp) = packet.as_udp() {
if let Ok(message) = Message::from_slice(udp.payload()) {
debug_assert!(
@@ -145,11 +141,11 @@ impl SimClient {
);
// Map back to upstream socket so we can assert on it correctly.
let sentinel = SocketAddr::from((packet.destination(), udp.get_destination()));
let sentinel = SocketAddr::from((packet.destination(), udp.destination_port()));
let upstream = self.upstream_dns_by_sentinel(&sentinel).unwrap();
self.sent_dns_queries
.insert((upstream, message.header().id()), packet);
.insert((upstream, message.header().id()), packet.clone());
}
}
}
@@ -178,29 +174,27 @@ impl SimClient {
}
/// Process an IP packet received on the client.
pub(crate) fn on_received_packet(&mut self, packet: IpPacket<'_>) {
pub(crate) fn on_received_packet(&mut self, packet: IpPacket<'static>) {
if let Some(icmp) = packet.as_icmp() {
let echo_reply = icmp.as_echo_reply().expect("to be echo reply");
let echo_reply = icmp.echo_reply_header().expect("to be echo reply");
self.received_icmp_replies.insert(
(echo_reply.sequence(), echo_reply.identifier()),
packet.to_owned(),
);
self.received_icmp_replies
.insert((echo_reply.seq, echo_reply.id), packet);
return;
};
if let Some(udp) = packet.as_udp() {
if udp.get_source() == 53 {
if udp.source_port() == 53 {
let message = Message::from_slice(udp.payload())
.expect("ip packets on port 53 to be DNS packets");
// Map back to upstream socket so we can assert on it correctly.
let sentinel = SocketAddr::from((packet.source(), udp.get_source()));
let sentinel = SocketAddr::from((packet.source(), udp.source_port()));
let upstream = self.upstream_dns_by_sentinel(&sentinel).unwrap();
self.received_dns_responses
.insert((upstream, message.header().id()), packet.to_owned());
.insert((upstream, message.header().id()), packet.clone());
for record in message.answer().unwrap() {
let record = record.unwrap();

View File

@@ -65,26 +65,25 @@ impl SimGateway {
fn on_received_packet(
&mut self,
global_dns_records: &BTreeMap<DomainName, BTreeSet<IpAddr>>,
packet: IpPacket<'_>,
packet: IpPacket<'static>,
now: Instant,
) -> Option<Transmit<'static>> {
let packet = packet.to_owned();
// TODO: Instead of handling these things inline, here, should we dispatch them via `RoutingTable`?
if let Some(icmp) = packet.as_icmp() {
if let Some(request) = icmp.as_echo_request() {
let payload = u64::from_be_bytes(*request.payload().first_chunk().unwrap());
tracing::debug!(%payload, "Received ICMP request");
if let Some(echo_request) = icmp.echo_request_header() {
let payload = icmp.payload();
let echo_id = u64::from_be_bytes(*payload.first_chunk().unwrap());
tracing::debug!(%echo_id, "Received ICMP request");
self.received_icmp_requests.insert(payload, packet.clone());
self.received_icmp_requests.insert(echo_id, packet.clone());
let echo_response = ip_packet::make::icmp_reply_packet(
packet.destination(),
packet.source(),
request.sequence(),
request.identifier(),
request.payload(),
echo_request.seq,
echo_request.id,
payload,
)
.expect("src and dst are taken from incoming packet");
let transmit = self

View File

@@ -7,15 +7,12 @@ pub struct Ipv4HeaderSliceMut<'a> {
impl<'a> Ipv4HeaderSliceMut<'a> {
/// Creates a new [`Ipv4HeaderSliceMut`].
///
/// # Safety
///
/// - The byte array must be at least of length 20.
/// - The IP version must be 4.
pub unsafe fn from_slice_unchecked(slice: &'a mut [u8]) -> Self {
debug_assert!(Ipv4HeaderSlice::from_slice(slice).is_ok()); // Debug asserts are no-ops in release mode, so this is still "unchecked".
pub fn from_slice(
slice: &'a mut [u8],
) -> Result<Self, etherparse::err::ipv4::HeaderSliceError> {
Ipv4HeaderSlice::from_slice(slice)?;
Self { slice }
Ok(Self { slice })
}
pub fn set_checksum(&mut self, checksum: u16) {

View File

@@ -7,15 +7,12 @@ pub struct Ipv6HeaderSliceMut<'a> {
impl<'a> Ipv6HeaderSliceMut<'a> {
/// Creates a new [`Ipv6HeaderSliceMut`].
///
/// # Safety
///
/// - The byte array must be at least of length 40.
/// - The IP version must be 6.
pub unsafe fn from_slice_unchecked(slice: &'a mut [u8]) -> Self {
debug_assert!(Ipv6HeaderSlice::from_slice(slice).is_ok()); // Debug asserts are no-ops in release mode, so this is still "unchecked".
pub fn from_slice(
slice: &'a mut [u8],
) -> Result<Self, etherparse::err::ipv6::HeaderSliceError> {
Ipv6HeaderSlice::from_slice(slice)?;
Self { slice }
Ok(Self { slice })
}
pub fn set_source(&mut self, src: [u8; 16]) {

View File

@@ -13,8 +13,10 @@ pub use pnet_packet::*;
#[cfg(all(test, feature = "proptest"))]
mod proptests;
use domain::base::Message;
use etherparse::{Ipv4Header, Ipv4HeaderSlice, Ipv6Header, Ipv6HeaderSlice};
use etherparse::{
IcmpEchoHeader, Icmpv4Slice, Icmpv4Type, Icmpv6Slice, Icmpv6Type, IpNumber, Ipv4Header,
Ipv4HeaderSlice, Ipv6Header, Ipv6HeaderSlice, TcpSlice, UdpSlice,
};
use ipv4_header_slice_mut::Ipv4HeaderSliceMut;
use ipv6_header_slice_mut::Ipv6HeaderSliceMut;
use pnet_packet::{
@@ -23,11 +25,8 @@ use pnet_packet::{
MutableIcmpPacket,
},
icmpv6::{Icmpv6Types, MutableIcmpv6Packet},
ip::{IpNextHeaderProtocol, IpNextHeaderProtocols},
ipv4::Ipv4Packet,
ipv6::Ipv6Packet,
tcp::{MutableTcpPacket, TcpPacket},
udp::{MutableUdpPacket, UdpPacket},
tcp::MutableTcpPacket,
udp::MutableUdpPacket,
};
use std::{
net::{IpAddr, Ipv4Addr, Ipv6Addr},
@@ -80,60 +79,77 @@ impl Protocol {
}
}
#[derive(Debug, PartialEq)]
pub enum IpPacket<'a> {
Ipv4(Ipv4Packet<'a>),
Ipv6(Ipv6Packet<'a>),
}
#[derive(Debug, PartialEq)]
pub enum IcmpPacket<'a> {
Ipv4(icmp::IcmpPacket<'a>),
Ipv6(icmpv6::Icmpv6Packet<'a>),
Ipv4(Icmpv4Slice<'a>),
Ipv6(Icmpv6Slice<'a>),
}
impl<'a> IcmpPacket<'a> {
pub fn icmp_type(&self) -> IcmpType {
match self {
IcmpPacket::Ipv4(v4) => IcmpType::V4(v4.get_icmp_type()),
IcmpPacket::Ipv6(v6) => IcmpType::V6(v6.get_icmpv6_type()),
IcmpPacket::Ipv4(v4) => IcmpType::V4(v4.icmp_type()),
IcmpPacket::Ipv6(v6) => IcmpType::V6(v6.icmp_type()),
}
}
pub fn identifier(&self) -> Option<u16> {
let request_id = self.as_echo_request().map(|r| r.identifier());
let reply_id = self.as_echo_reply().map(|r| r.identifier());
request_id.or(reply_id)
Some(self.echo_request_header().or(self.echo_reply_header())?.id)
}
pub fn sequence(&self) -> Option<u16> {
let request_id = self.as_echo_request().map(|r| r.sequence());
let reply_id = self.as_echo_reply().map(|r| r.sequence());
Some(self.echo_request_header().or(self.echo_reply_header())?.seq)
}
request_id.or(reply_id)
pub fn payload(&self) -> &[u8] {
match self {
IcmpPacket::Ipv4(v4) => v4.payload(),
IcmpPacket::Ipv6(v6) => v6.payload(),
}
}
pub fn echo_request_header(&self) -> Option<IcmpEchoHeader> {
#[allow(
clippy::wildcard_enum_match_arm,
reason = "We won't ever need to use other ICMP types here."
)]
match self {
IcmpPacket::Ipv4(v4) => match v4.header().icmp_type {
Icmpv4Type::EchoRequest(echo) => Some(echo),
_ => None,
},
IcmpPacket::Ipv6(v6) => match v6.header().icmp_type {
Icmpv6Type::EchoRequest(echo) => Some(echo),
_ => None,
},
}
}
pub fn echo_reply_header(&self) -> Option<IcmpEchoHeader> {
#[allow(
clippy::wildcard_enum_match_arm,
reason = "We won't ever need to use other ICMP types here."
)]
match self {
IcmpPacket::Ipv4(v4) => match v4.header().icmp_type {
Icmpv4Type::EchoReply(echo) => Some(echo),
_ => None,
},
IcmpPacket::Ipv6(v6) => match v6.header().icmp_type {
Icmpv6Type::EchoReply(echo) => Some(echo),
_ => None,
},
}
}
}
pub enum IcmpType {
V4(icmp::IcmpType),
V6(icmpv6::Icmpv6Type),
}
#[derive(Debug, PartialEq)]
pub enum IcmpEchoRequest<'a> {
Ipv4(icmp::echo_request::EchoRequestPacket<'a>),
Ipv6(icmpv6::echo_request::EchoRequestPacket<'a>),
}
#[derive(Debug, PartialEq)]
pub enum IcmpEchoReply<'a> {
Ipv4(icmp::echo_reply::EchoReplyPacket<'a>),
Ipv6(icmpv6::echo_reply::EchoReplyPacket<'a>),
V4(Icmpv4Type),
V6(Icmpv6Type),
}
#[derive(Debug, PartialEq, Clone)]
pub enum MutableIpPacket<'a> {
pub enum IpPacket<'a> {
Ipv4(ConvertibleIpv4Packet<'a>),
Ipv6(ConvertibleIpv6Packet<'a>),
}
@@ -206,17 +222,11 @@ impl<'a> ConvertibleIpv4Packet<'a> {
}
fn ip_header(&self) -> Ipv4HeaderSlice {
// TODO: Make `_unchecked` variant public upstream.
Ipv4HeaderSlice::from_slice(&self.buf[20..]).expect("we checked this during `new`")
}
fn ip_header_mut(&mut self) -> Ipv4HeaderSliceMut {
// Safety: We checked this in `new` / `owned`.
unsafe { Ipv4HeaderSliceMut::from_slice_unchecked(&mut self.buf[20..]) }
}
pub fn to_immutable(&self) -> Ipv4Packet {
Ipv4Packet::new(&self.buf[20..]).expect("when constructed we checked that this is some")
Ipv4HeaderSliceMut::from_slice(&mut self.buf[20..]).expect("we checked this during `new`")
}
pub fn get_source(&self) -> Ipv4Addr {
@@ -227,18 +237,6 @@ impl<'a> ConvertibleIpv4Packet<'a> {
self.ip_header().destination_addr()
}
fn consume_to_immutable(self) -> Ipv4Packet<'a> {
match self.buf {
MaybeOwned::RefMut(buf) => {
Ipv4Packet::new(&buf[20..]).expect("when constructed we checked that this is some")
}
MaybeOwned::Owned(mut owned) => {
owned.drain(..20);
Ipv4Packet::owned(owned).expect("when constructed we checked that this is some")
}
}
}
fn consume_to_ipv6(
mut self,
src: Ipv6Addr,
@@ -301,17 +299,11 @@ impl<'a> ConvertibleIpv6Packet<'a> {
}
fn header(&self) -> Ipv6HeaderSlice {
// FIXME: Make the `_unchecked` variant public upstream.
Ipv6HeaderSlice::from_slice(&self.buf).expect("We checked this in `new` / `owned`")
}
fn header_mut(&mut self) -> Ipv6HeaderSliceMut {
// Safety: We checked this in `new` / `owned`.
unsafe { Ipv6HeaderSliceMut::from_slice_unchecked(&mut self.buf) }
}
fn to_immutable(&self) -> Ipv6Packet {
Ipv6Packet::new(&self.buf).expect("when constructed we checked that this is some")
Ipv6HeaderSliceMut::from_slice(&mut self.buf).expect("We checked this in `new` / `owned`")
}
pub fn get_source(&self) -> Ipv6Addr {
@@ -322,17 +314,6 @@ impl<'a> ConvertibleIpv6Packet<'a> {
self.header().destination_addr()
}
fn consume_to_immutable(self) -> Ipv6Packet<'a> {
match self.buf {
MaybeOwned::RefMut(buf) => {
Ipv6Packet::new(buf).expect("when constructed we checked that this is some")
}
MaybeOwned::Owned(owned) => {
Ipv6Packet::owned(owned).expect("when constructed we checked that this is some")
}
}
}
fn consume_to_ipv4(
mut self,
src: Ipv4Addr,
@@ -398,19 +379,17 @@ pub fn ipv6_translated(ip: Ipv6Addr) -> Option<Ipv4Addr> {
))
}
impl<'a> MutableIpPacket<'a> {
impl<'a> IpPacket<'a> {
// TODO: this API is a bit akward, since you have to pass the extra prepended 20 bytes
pub fn new(buf: &'a mut [u8]) -> Option<Self> {
match buf[20] >> 4 {
4 => Some(MutableIpPacket::Ipv4(ConvertibleIpv4Packet::new(buf)?)),
6 => Some(MutableIpPacket::Ipv6(ConvertibleIpv6Packet::new(
&mut buf[20..],
)?)),
4 => Some(IpPacket::Ipv4(ConvertibleIpv4Packet::new(buf)?)),
6 => Some(IpPacket::Ipv6(ConvertibleIpv6Packet::new(&mut buf[20..])?)),
_ => None,
}
}
pub(crate) fn owned(mut data: Vec<u8>) -> Option<MutableIpPacket<'static>> {
pub(crate) fn owned(mut data: Vec<u8>) -> Option<IpPacket<'static>> {
let packet = match data[20] >> 4 {
4 => ConvertibleIpv4Packet::owned(data)?.into(),
6 => {
@@ -423,33 +402,28 @@ impl<'a> MutableIpPacket<'a> {
Some(packet)
}
pub fn to_immutable(&self) -> IpPacket {
for_both!(self, |i| i.to_immutable().into())
}
pub(crate) fn consume_to_ipv4(
self,
src: Ipv4Addr,
dst: Ipv4Addr,
) -> Option<MutableIpPacket<'a>> {
pub fn to_owned(&self) -> IpPacket<'static> {
match self {
MutableIpPacket::Ipv4(pkt) => Some(MutableIpPacket::Ipv4(pkt)),
MutableIpPacket::Ipv6(pkt) => {
Some(MutableIpPacket::Ipv4(pkt.consume_to_ipv4(src, dst)?))
}
IpPacket::Ipv4(i) => IpPacket::Ipv4(ConvertibleIpv4Packet {
buf: MaybeOwned::Owned(i.buf.to_vec()),
}),
IpPacket::Ipv6(i) => IpPacket::Ipv6(ConvertibleIpv6Packet {
buf: MaybeOwned::Owned(i.buf.to_vec()),
}),
}
}
pub(crate) fn consume_to_ipv6(
self,
src: Ipv6Addr,
dst: Ipv6Addr,
) -> Option<MutableIpPacket<'a>> {
pub(crate) fn consume_to_ipv4(self, src: Ipv4Addr, dst: Ipv4Addr) -> Option<IpPacket<'a>> {
match self {
MutableIpPacket::Ipv4(pkt) => {
Some(MutableIpPacket::Ipv6(pkt.consume_to_ipv6(src, dst)?))
}
MutableIpPacket::Ipv6(pkt) => Some(MutableIpPacket::Ipv6(pkt)),
IpPacket::Ipv4(pkt) => Some(IpPacket::Ipv4(pkt)),
IpPacket::Ipv6(pkt) => Some(IpPacket::Ipv4(pkt.consume_to_ipv4(src, dst)?)),
}
}
pub(crate) fn consume_to_ipv6(self, src: Ipv6Addr, dst: Ipv6Addr) -> Option<IpPacket<'a>> {
match self {
IpPacket::Ipv4(pkt) => Some(IpPacket::Ipv6(pkt.consume_to_ipv6(src, dst)?)),
IpPacket::Ipv6(pkt) => Some(IpPacket::Ipv6(pkt)),
}
}
@@ -461,12 +435,58 @@ impl<'a> MutableIpPacket<'a> {
for_both!(self, |i| i.get_destination().into())
}
pub fn source_protocol(&self) -> Result<Protocol, UnsupportedProtocol> {
if let Some(p) = self.as_tcp() {
return Ok(Protocol::Tcp(p.source_port()));
}
if let Some(p) = self.as_udp() {
return Ok(Protocol::Udp(p.source_port()));
}
if let Some(p) = self.as_icmp() {
let id = p.identifier().ok_or_else(|| match p.icmp_type() {
IcmpType::V4(v4) => UnsupportedProtocol::UnsupportedIcmpv4Type(v4),
IcmpType::V6(v6) => UnsupportedProtocol::UnsupportedIcmpv6Type(v6),
})?;
return Ok(Protocol::Icmp(id));
}
Err(UnsupportedProtocol::UnsupportedIpPayload(
self.next_header(),
))
}
pub fn destination_protocol(&self) -> Result<Protocol, UnsupportedProtocol> {
if let Some(p) = self.as_tcp() {
return Ok(Protocol::Tcp(p.destination_port()));
}
if let Some(p) = self.as_udp() {
return Ok(Protocol::Udp(p.destination_port()));
}
if let Some(p) = self.as_icmp() {
let id = p.identifier().ok_or_else(|| match p.icmp_type() {
IcmpType::V4(v4) => UnsupportedProtocol::UnsupportedIcmpv4Type(v4),
IcmpType::V6(v6) => UnsupportedProtocol::UnsupportedIcmpv6Type(v6),
})?;
return Ok(Protocol::Icmp(id));
}
Err(UnsupportedProtocol::UnsupportedIpPayload(
self.next_header(),
))
}
pub fn set_source_protocol(&mut self, v: u16) {
if let Some(mut p) = self.as_tcp() {
if let Some(mut p) = self.as_tcp_mut() {
p.set_source(v);
}
if let Some(mut p) = self.as_udp() {
if let Some(mut p) = self.as_udp_mut() {
p.set_source(v);
}
@@ -474,11 +494,11 @@ impl<'a> MutableIpPacket<'a> {
}
pub fn set_destination_protocol(&mut self, v: u16) {
if let Some(mut p) = self.as_tcp() {
if let Some(mut p) = self.as_tcp_mut() {
p.set_destination(v);
}
if let Some(mut p) = self.as_udp() {
if let Some(mut p) = self.as_udp_mut() {
p.set_destination(v);
}
@@ -486,7 +506,7 @@ impl<'a> MutableIpPacket<'a> {
}
fn set_icmp_identifier(&mut self, v: u16) {
if let Some(mut p) = self.as_icmp() {
if let Some(mut p) = self.as_icmp_mut() {
if p.get_icmp_type() == IcmpTypes::EchoReply {
let Some(mut echo_reply) = MutableEchoReplyPacket::new(p.packet_mut()) else {
return;
@@ -536,68 +556,89 @@ impl<'a> MutableIpPacket<'a> {
}
fn set_ipv4_checksum(&mut self) {
if let Self::Ipv4(p) = self {
let checksum = ipv4::checksum(&p.to_immutable());
p.ip_header_mut().set_checksum(checksum);
}
}
fn set_udp_checksum(&mut self) {
let checksum = if let Some(p) = self.as_immutable_udp() {
self.to_immutable().udp_checksum(&p.to_immutable())
} else {
let Self::Ipv4(p) = self else {
return;
};
self.as_udp()
let checksum = p.ip_header().to_header().calc_header_checksum();
p.ip_header_mut().set_checksum(checksum);
}
fn set_udp_checksum(&mut self) {
let Some(udp) = self.as_udp() else {
return;
};
let checksum = match &self {
IpPacket::Ipv4(v4) => udp
.to_header()
.calc_checksum_ipv4(&v4.ip_header().to_header(), udp.payload()),
IpPacket::Ipv6(v6) => udp
.to_header()
.calc_checksum_ipv6(&v6.header().to_header(), udp.payload()),
}
.expect("size of payload was previously checked to be okay");
self.as_udp_mut()
.expect("Developer error: we can only get a UDP checksum if the packet is udp")
.set_checksum(checksum);
}
fn set_tcp_checksum(&mut self) {
let checksum = if let Some(p) = self.as_immutable_tcp() {
self.to_immutable().tcp_checksum(&p.to_immutable())
} else {
let Some(tcp) = self.as_tcp() else {
return;
};
self.as_tcp()
.expect("Developer error: we can only get a TCP checksum if the packet is tcp")
let checksum = match &self {
IpPacket::Ipv4(v4) => tcp
.to_header()
.calc_checksum_ipv4(&v4.ip_header().to_header(), tcp.payload()),
IpPacket::Ipv6(v6) => tcp
.to_header()
.calc_checksum_ipv6(&v6.header().to_header(), tcp.payload()),
}
.expect("size of payload was previously checked to be okay");
self.as_tcp_mut()
.expect("Developer error: we can only get a UDP checksum if the packet is udp")
.set_checksum(checksum);
}
pub fn into_immutable(self) -> IpPacket<'a> {
match self {
Self::Ipv4(p) => p.consume_to_immutable().into(),
Self::Ipv6(p) => p.consume_to_immutable().into(),
}
pub fn as_udp(&self) -> Option<UdpSlice> {
self.is_udp()
.then(|| UdpSlice::from_slice(self.payload()).ok())
.flatten()
}
pub fn as_immutable(&self) -> IpPacket<'_> {
match self {
Self::Ipv4(p) => IpPacket::Ipv4(p.to_immutable()),
Self::Ipv6(p) => IpPacket::Ipv6(p.to_immutable()),
}
}
pub fn as_udp(&mut self) -> Option<MutableUdpPacket> {
self.to_immutable()
.is_udp()
pub fn as_udp_mut(&mut self) -> Option<MutableUdpPacket> {
self.is_udp()
.then(|| MutableUdpPacket::new(self.payload_mut()))
.flatten()
}
fn as_tcp(&mut self) -> Option<MutableTcpPacket> {
self.to_immutable()
.is_tcp()
pub fn as_tcp(&self) -> Option<TcpSlice> {
self.is_tcp()
.then(|| TcpSlice::from_slice(self.payload()).ok())
.flatten()
}
pub fn as_tcp_mut(&mut self) -> Option<MutableTcpPacket> {
self.is_tcp()
.then(|| MutableTcpPacket::new(self.payload_mut()))
.flatten()
}
pub fn is_icmp_v4_or_v6(&self) -> bool {
match self {
IpPacket::Ipv4(v4) => v4.ip_header().protocol() == IpNumber::ICMP,
IpPacket::Ipv6(v6) => v6.header().next_header() == IpNumber::IPV6_ICMP,
}
}
fn set_icmpv6_checksum(&mut self) {
let (src_addr, dst_addr) = match self {
MutableIpPacket::Ipv4(_) => return,
MutableIpPacket::Ipv6(p) => (p.get_source(), p.get_destination()),
IpPacket::Ipv4(_) => return,
IpPacket::Ipv6(p) => (p.get_source(), p.get_destination()),
};
if let Some(mut pkt) = self.as_icmpv6() {
let checksum = icmpv6::checksum(&pkt.to_immutable(), &src_addr, &dst_addr);
@@ -606,50 +647,46 @@ impl<'a> MutableIpPacket<'a> {
}
fn set_icmpv4_checksum(&mut self) {
if let Some(mut pkt) = self.as_icmp() {
if let Some(mut pkt) = self.as_icmp_mut() {
let checksum = icmp::checksum(&pkt.to_immutable());
pkt.set_checksum(checksum);
}
}
fn as_icmp(&mut self) -> Option<MutableIcmpPacket> {
self.to_immutable()
.is_icmp()
pub fn as_icmp(&self) -> Option<IcmpPacket> {
match self {
Self::Ipv4(v4) if self.is_icmp() => Some(IcmpPacket::Ipv4(
Icmpv4Slice::from_slice(v4.payload()).ok()?,
)),
Self::Ipv6(v6) if self.is_icmpv6() => Some(IcmpPacket::Ipv6(
Icmpv6Slice::from_slice(v6.payload()).ok()?,
)),
Self::Ipv4(_) | Self::Ipv6(_) => None,
}
}
pub fn as_icmp_mut(&mut self) -> Option<MutableIcmpPacket> {
self.is_icmp()
.then(|| MutableIcmpPacket::new(self.payload_mut()))
.flatten()
}
fn as_icmpv6(&mut self) -> Option<MutableIcmpv6Packet> {
self.to_immutable()
.is_icmpv6()
self.is_icmpv6()
.then(|| MutableIcmpv6Packet::new(self.payload_mut()))
.flatten()
}
fn as_immutable_udp(&self) -> Option<UdpPacket> {
self.to_immutable()
.is_udp()
.then(|| UdpPacket::new(self.payload()))
.flatten()
}
fn as_immutable_tcp(&self) -> Option<TcpPacket> {
self.to_immutable()
.is_tcp()
.then(|| TcpPacket::new(self.payload()))
.flatten()
}
pub fn translate_destination(
mut self,
src_v4: Ipv4Addr,
src_v6: Ipv6Addr,
src_proto: Protocol,
dst: IpAddr,
) -> Option<MutableIpPacket<'a>> {
) -> Option<IpPacket<'a>> {
let mut packet = match (&self, dst) {
(&MutableIpPacket::Ipv4(_), IpAddr::V6(dst)) => self.consume_to_ipv6(src_v6, dst)?,
(&MutableIpPacket::Ipv6(_), IpAddr::V4(dst)) => self.consume_to_ipv4(src_v4, dst)?,
(&IpPacket::Ipv4(_), IpAddr::V6(dst)) => self.consume_to_ipv6(src_v6, dst)?,
(&IpPacket::Ipv6(_), IpAddr::V4(dst)) => self.consume_to_ipv4(src_v4, dst)?,
_ => {
self.set_dst(dst);
self
@@ -666,10 +703,10 @@ impl<'a> MutableIpPacket<'a> {
dst_v6: Ipv6Addr,
dst_proto: Protocol,
src: IpAddr,
) -> Option<MutableIpPacket<'a>> {
) -> Option<IpPacket<'a>> {
let mut packet = match (&self, src) {
(&MutableIpPacket::Ipv4(_), IpAddr::V6(src)) => self.consume_to_ipv6(src, dst_v6)?,
(&MutableIpPacket::Ipv6(_), IpAddr::V4(src)) => self.consume_to_ipv4(src, dst_v4)?,
(&IpPacket::Ipv4(_), IpAddr::V6(src)) => self.consume_to_ipv6(src, dst_v6)?,
(&IpPacket::Ipv6(_), IpAddr::V4(src)) => self.consume_to_ipv4(src, dst_v4)?,
_ => {
self.set_src(src);
self
@@ -715,43 +752,22 @@ impl<'a> MutableIpPacket<'a> {
}
}
}
}
impl<'a> IpPacket<'a> {
pub fn new(buf: &'a [u8]) -> Option<Self> {
match buf[0] >> 4 {
4 => Some(IpPacket::Ipv4(Ipv4Packet::new(buf)?)),
6 => Some(IpPacket::Ipv6(Ipv6Packet::new(buf)?)),
_ => None,
}
}
pub fn to_owned(&self) -> IpPacket<'static> {
match self {
IpPacket::Ipv4(i) => Ipv4Packet::owned(i.packet().to_vec())
.expect("owned packet should still be valid")
.into(),
IpPacket::Ipv6(i) => Ipv6Packet::owned(i.packet().to_vec())
.expect("owned packet should still be valid")
.into(),
}
}
pub fn ipv4_header(&self) -> Option<Ipv4Header> {
match self {
IpPacket::Ipv4(p) => Some(
Self::Ipv4(p) => Some(
Ipv4HeaderSlice::from_slice(p.packet())
.expect("Should be a valid packet")
.to_header(),
),
IpPacket::Ipv6(_) => None,
Self::Ipv6(_) => None,
}
}
pub fn ipv6_header(&self) -> Option<Ipv6Header> {
match self {
IpPacket::Ipv4(_) => None,
IpPacket::Ipv6(p) => Some(
Self::Ipv4(_) => None,
Self::Ipv6(p) => Some(
Ipv6HeaderSlice::from_slice(p.packet())
.expect("Should be a valid packet")
.to_header(),
@@ -759,255 +775,42 @@ impl<'a> IpPacket<'a> {
}
}
pub fn source_protocol(&self) -> Result<Protocol, UnsupportedProtocol> {
if let Some(p) = self.as_tcp() {
return Ok(Protocol::Tcp(p.get_source()));
}
if let Some(p) = self.as_udp() {
return Ok(Protocol::Udp(p.get_source()));
}
if let Some(p) = self.as_icmp() {
let id = p.identifier().ok_or_else(|| match p.icmp_type() {
IcmpType::V4(v4) => UnsupportedProtocol::UnsupportedIcmpv4Type(v4.0),
IcmpType::V6(v6) => UnsupportedProtocol::UnsupportedIcmpv6Type(v6.0),
})?;
return Ok(Protocol::Icmp(id));
}
Err(UnsupportedProtocol::UnsupportedIpPayload(
self.next_header(),
))
}
pub fn destination_protocol(&self) -> Result<Protocol, UnsupportedProtocol> {
if let Some(p) = self.as_tcp() {
return Ok(Protocol::Tcp(p.get_destination()));
}
if let Some(p) = self.as_udp() {
return Ok(Protocol::Udp(p.get_destination()));
}
if let Some(p) = self.as_icmp() {
let id = p.identifier().ok_or_else(|| match p.icmp_type() {
IcmpType::V4(v4) => UnsupportedProtocol::UnsupportedIcmpv4Type(v4.0),
IcmpType::V6(v6) => UnsupportedProtocol::UnsupportedIcmpv6Type(v6.0),
})?;
return Ok(Protocol::Icmp(id));
}
Err(UnsupportedProtocol::UnsupportedIpPayload(
self.next_header(),
))
}
pub fn source(&self) -> IpAddr {
for_both!(self, |i| i.get_source().into())
}
pub fn destination(&self) -> IpAddr {
for_both!(self, |i| i.get_destination().into())
}
pub fn next_header(&self) -> IpNextHeaderProtocol {
fn next_header(&self) -> IpNumber {
match self {
Self::Ipv4(p) => p.get_next_level_protocol(),
Self::Ipv6(p) => p.get_next_header(),
Self::Ipv4(p) => p.ip_header().protocol(),
Self::Ipv6(p) => p.header().next_header(),
}
}
fn is_udp(&self) -> bool {
self.next_header() == IpNextHeaderProtocols::Udp
self.next_header() == IpNumber::UDP
}
fn is_tcp(&self) -> bool {
self.next_header() == IpNextHeaderProtocols::Tcp
self.next_header() == IpNumber::TCP
}
fn is_icmp(&self) -> bool {
self.next_header() == IpNextHeaderProtocols::Icmp
self.next_header() == IpNumber::ICMP
}
fn is_icmpv6(&self) -> bool {
self.next_header() == IpNextHeaderProtocols::Icmpv6
}
pub fn as_udp(&self) -> Option<UdpPacket> {
self.is_udp()
.then(|| UdpPacket::new(self.payload()))
.flatten()
}
/// Unwrap this [`IpPacket`] as a [`UdpPacket`], panicking in case it is not.
pub fn unwrap_as_udp(&self) -> UdpPacket {
self.as_udp().expect("Packet is not a UDP packet")
}
/// Unwrap this [`IpPacket`] as a DNS message, panicking in case it is not.
pub fn unwrap_as_dns(&self) -> Message<Vec<u8>> {
let udp = self.unwrap_as_udp();
let message = match Message::from_octets(udp.payload().to_vec()) {
Ok(message) => message,
Err(e) => {
panic!("Failed to parse UDP payload as DNS message: {e}");
}
};
message
}
pub fn as_tcp(&self) -> Option<TcpPacket> {
self.is_tcp()
.then(|| TcpPacket::new(self.payload()))
.flatten()
}
pub fn as_icmp(&self) -> Option<IcmpPacket> {
match self {
IpPacket::Ipv4(v4) if v4.get_next_level_protocol() == IpNextHeaderProtocols::Icmp => {
Some(IcmpPacket::Ipv4(pnet_packet::icmp::IcmpPacket::new(
v4.payload(),
)?))
}
IpPacket::Ipv6(v6) if v6.get_next_header() == IpNextHeaderProtocols::Icmpv6 => {
Some(IcmpPacket::Ipv6(icmpv6::Icmpv6Packet::new(v6.payload())?))
}
IpPacket::Ipv4(_) | IpPacket::Ipv6(_) => None,
}
}
fn udp_checksum(&self, dgm: &UdpPacket<'_>) -> u16 {
match self {
Self::Ipv4(p) => udp::ipv4_checksum(dgm, &p.get_source(), &p.get_destination()),
Self::Ipv6(p) => udp::ipv6_checksum(dgm, &p.get_source(), &p.get_destination()),
}
}
fn tcp_checksum(&self, pkt: &TcpPacket<'_>) -> u16 {
match self {
Self::Ipv4(p) => tcp::ipv4_checksum(pkt, &p.get_source(), &p.get_destination()),
Self::Ipv6(p) => tcp::ipv6_checksum(pkt, &p.get_source(), &p.get_destination()),
}
self.next_header() == IpNumber::IPV6_ICMP
}
}
impl<'a> IcmpPacket<'a> {
pub fn as_echo_request(&self) -> Option<IcmpEchoRequest> {
match self {
IcmpPacket::Ipv4(v4) if matches!(v4.get_icmp_type(), icmp::IcmpTypes::EchoRequest) => {
Some(IcmpEchoRequest::Ipv4(
icmp::echo_request::EchoRequestPacket::new(v4.packet())?,
))
}
IcmpPacket::Ipv6(v6)
if matches!(v6.get_icmpv6_type(), icmpv6::Icmpv6Types::EchoRequest) =>
{
Some(IcmpEchoRequest::Ipv6(
icmpv6::echo_request::EchoRequestPacket::new(v6.packet())?,
))
}
IcmpPacket::Ipv4(_) | IcmpPacket::Ipv6(_) => None,
}
}
pub fn as_echo_reply(&self) -> Option<IcmpEchoReply> {
match self {
IcmpPacket::Ipv4(v4) if matches!(v4.get_icmp_type(), icmp::IcmpTypes::EchoReply) => {
Some(IcmpEchoReply::Ipv4(icmp::echo_reply::EchoReplyPacket::new(
v4.packet(),
)?))
}
IcmpPacket::Ipv6(v6)
if matches!(v6.get_icmpv6_type(), icmpv6::Icmpv6Types::EchoReply) =>
{
Some(IcmpEchoReply::Ipv6(
icmpv6::echo_reply::EchoReplyPacket::new(v6.packet())?,
))
}
IcmpPacket::Ipv4(_) | IcmpPacket::Ipv6(_) => None,
}
}
pub fn is_echo_reply(&self) -> bool {
self.as_echo_reply().is_some()
}
pub fn is_echo_request(&self) -> bool {
self.as_echo_request().is_some()
}
}
impl<'a> IcmpEchoRequest<'a> {
pub fn sequence(&self) -> u16 {
for_both!(self, |i| i.get_sequence_number())
}
pub fn identifier(&self) -> u16 {
for_both!(self, |i| i.get_identifier())
}
pub fn payload(&self) -> &[u8] {
for_both!(self, |i| i.payload())
}
}
impl<'a> IcmpEchoReply<'a> {
pub fn sequence(&self) -> u16 {
for_both!(self, |i| i.get_sequence_number())
}
pub fn identifier(&self) -> u16 {
for_both!(self, |i| i.get_identifier())
}
}
impl Clone for IpPacket<'static> {
fn clone(&self) -> Self {
match self {
Self::Ipv4(ip4) => Self::Ipv4(Ipv4Packet::owned(ip4.packet().to_vec()).unwrap()),
Self::Ipv6(ip6) => Self::Ipv6(Ipv6Packet::owned(ip6.packet().to_vec()).unwrap()),
}
}
}
impl<'a> From<Ipv4Packet<'a>> for IpPacket<'a> {
fn from(value: Ipv4Packet<'a>) -> Self {
Self::Ipv4(value)
}
}
impl<'a> From<Ipv6Packet<'a>> for IpPacket<'a> {
fn from(value: Ipv6Packet<'a>) -> Self {
Self::Ipv6(value)
}
}
impl<'a> From<ConvertibleIpv4Packet<'a>> for MutableIpPacket<'a> {
impl<'a> From<ConvertibleIpv4Packet<'a>> for IpPacket<'a> {
fn from(value: ConvertibleIpv4Packet<'a>) -> Self {
Self::Ipv4(value)
}
}
impl<'a> From<ConvertibleIpv6Packet<'a>> for MutableIpPacket<'a> {
impl<'a> From<ConvertibleIpv6Packet<'a>> for IpPacket<'a> {
fn from(value: ConvertibleIpv6Packet<'a>) -> Self {
Self::Ipv6(value)
}
}
impl pnet_packet::Packet for MutableIpPacket<'_> {
fn packet(&self) -> &[u8] {
for_both!(self, |i| i.packet())
}
fn payload(&self) -> &[u8] {
for_both!(self, |i| i.payload())
}
}
impl pnet_packet::Packet for IpPacket<'_> {
fn packet(&self) -> &[u8] {
for_both!(self, |i| i.packet())
@@ -1018,7 +821,7 @@ impl pnet_packet::Packet for IpPacket<'_> {
}
}
impl pnet_packet::MutablePacket for MutableIpPacket<'_> {
impl pnet_packet::MutablePacket for IpPacket<'_> {
fn packet_mut(&mut self) -> &mut [u8] {
for_both!(self, |i| i.packet_mut())
}
@@ -1028,21 +831,12 @@ impl pnet_packet::MutablePacket for MutableIpPacket<'_> {
}
}
impl<'a> PacketSize for IpPacket<'a> {
fn packet_size(&self) -> usize {
match self {
Self::Ipv4(p) => p.packet_size(),
Self::Ipv6(p) => p.packet_size(),
}
}
}
#[derive(Debug, thiserror::Error)]
pub enum UnsupportedProtocol {
#[error("Unsupported IP protocol: {0}")]
UnsupportedIpPayload(IpNextHeaderProtocol),
#[error("Unsupported ICMPv4 type: {0}")]
UnsupportedIcmpv4Type(u8),
#[error("Unsupported ICMPv6 type: {0}")]
UnsupportedIcmpv6Type(u8),
#[error("Unsupported IP protocol: {0:?}")]
UnsupportedIpPayload(IpNumber),
#[error("Unsupported ICMPv4 type: {0:?}")]
UnsupportedIcmpv4Type(Icmpv4Type),
#[error("Unsupported ICMPv6 type: {0:?}")]
UnsupportedIcmpv6Type(Icmpv6Type),
}

View File

@@ -1,17 +1,17 @@
//! Factory module for making all kinds of packets.
use crate::{IpPacket, MutableIpPacket};
use crate::IpPacket;
use domain::{
base::{
iana::{Class, Opcode, Rcode},
MessageBuilder, Name, Question, Record, Rtype, ToName, Ttl,
Message, MessageBuilder, Name, Question, Record, Rtype, ToName, Ttl,
},
rdata::AllRecordData,
};
use etherparse::PacketBuilder;
use std::net::{IpAddr, SocketAddr};
/// Helper macro to turn a [`PacketBuilder`] into a [`MutableIpPacket`].
/// Helper macro to turn a [`PacketBuilder`] into an [`IpPacket`].
#[macro_export]
macro_rules! build {
($packet:expr, $payload:ident) => {{
@@ -22,7 +22,7 @@ macro_rules! build {
.write(&mut std::io::Cursor::new(&mut buf[20..]), &$payload)
.expect("Buffer should be big enough");
MutableIpPacket::owned(buf).expect("Should be a valid IP packet")
IpPacket::owned(buf).expect("Should be a valid IP packet")
}};
}
@@ -32,7 +32,7 @@ pub fn icmp_request_packet(
seq: u16,
identifier: u16,
payload: &[u8],
) -> Result<MutableIpPacket<'static>, IpVersionMismatch> {
) -> Result<IpPacket<'static>, IpVersionMismatch> {
match (src, dst.into()) {
(IpAddr::V4(src), IpAddr::V4(dst)) => {
let packet = PacketBuilder::ipv4(src.octets(), dst.octets(), 64)
@@ -56,7 +56,7 @@ pub fn icmp_reply_packet(
seq: u16,
identifier: u16,
payload: &[u8],
) -> Result<MutableIpPacket<'static>, IpVersionMismatch> {
) -> Result<IpPacket<'static>, IpVersionMismatch> {
match (src, dst.into()) {
(IpAddr::V4(src), IpAddr::V4(dst)) => {
let packet = PacketBuilder::ipv4(src.octets(), dst.octets(), 64)
@@ -80,7 +80,7 @@ pub fn tcp_packet<IP>(
sport: u16,
dport: u16,
payload: Vec<u8>,
) -> Result<MutableIpPacket<'static>, IpVersionMismatch>
) -> Result<IpPacket<'static>, IpVersionMismatch>
where
IP: Into<IpAddr>,
{
@@ -107,7 +107,7 @@ pub fn udp_packet<IP>(
sport: u16,
dport: u16,
payload: Vec<u8>,
) -> Result<MutableIpPacket<'static>, IpVersionMismatch>
) -> Result<IpPacket<'static>, IpVersionMismatch>
where
IP: Into<IpAddr>,
{
@@ -132,7 +132,7 @@ pub fn dns_query(
src: SocketAddr,
dst: SocketAddr,
id: u16,
) -> Result<MutableIpPacket<'static>, IpVersionMismatch> {
) -> Result<IpPacket<'static>, IpVersionMismatch> {
// Create the DNS query message
let mut msg_builder = MessageBuilder::new_vec();
@@ -155,12 +155,12 @@ pub fn dns_query(
pub fn dns_ok_response<I>(
packet: IpPacket<'static>,
resolve: impl Fn(&Name<Vec<u8>>) -> I,
) -> MutableIpPacket<'static>
) -> IpPacket<'static>
where
I: Iterator<Item = IpAddr>,
{
let udp = packet.unwrap_as_udp();
let query = packet.unwrap_as_dns();
let udp = packet.as_udp().unwrap();
let query = Message::from_octets(udp.payload().to_vec()).unwrap();
let response = MessageBuilder::new_vec();
let mut answers = response.start_answer(&query, Rcode::NOERROR).unwrap();
@@ -194,8 +194,8 @@ where
udp_packet(
packet.destination(),
packet.source(),
udp.get_destination(),
udp.get_source(),
udp.destination_port(),
udp.source_port(),
payload,
)
.expect("src and dst are retrieved from the same packet")

View File

@@ -1,8 +1,8 @@
use crate::MutableIpPacket;
use crate::IpPacket;
use proptest::{arbitrary::any, prop_oneof, strategy::Strategy};
use std::net::{IpAddr, Ipv4Addr, Ipv6Addr};
pub fn udp_packet() -> impl Strategy<Value = MutableIpPacket<'static>> {
pub fn udp_packet() -> impl Strategy<Value = IpPacket<'static>> {
prop_oneof![
(ip4_tuple(), any::<u16>(), any::<u16>()).prop_map(|((saddr, daddr), sport, dport)| {
crate::make::udp_packet(saddr, daddr, sport, dport, Vec::new()).unwrap()
@@ -13,7 +13,7 @@ pub fn udp_packet() -> impl Strategy<Value = MutableIpPacket<'static>> {
]
}
pub fn tcp_packet() -> impl Strategy<Value = MutableIpPacket<'static>> {
pub fn tcp_packet() -> impl Strategy<Value = IpPacket<'static>> {
prop_oneof![
(ip4_tuple(), any::<u16>(), any::<u16>()).prop_map(|((saddr, daddr), sport, dport)| {
crate::make::tcp_packet(saddr, daddr, sport, dport, Vec::new()).unwrap()
@@ -24,7 +24,7 @@ pub fn tcp_packet() -> impl Strategy<Value = MutableIpPacket<'static>> {
]
}
pub fn icmp_request_packet() -> impl Strategy<Value = MutableIpPacket<'static>> {
pub fn icmp_request_packet() -> impl Strategy<Value = IpPacket<'static>> {
prop_oneof![
(ip4_tuple(), any::<u16>(), any::<u16>()).prop_map(|((saddr, daddr), sport, dport)| {
crate::make::icmp_request_packet(IpAddr::V4(saddr), daddr, sport, dport, &[]).unwrap()
@@ -35,7 +35,7 @@ pub fn icmp_request_packet() -> impl Strategy<Value = MutableIpPacket<'static>>
]
}
pub fn udp_or_tcp_or_icmp_packet() -> impl Strategy<Value = MutableIpPacket<'static>> {
pub fn udp_or_tcp_or_icmp_packet() -> impl Strategy<Value = IpPacket<'static>> {
prop_oneof![udp_packet(), tcp_packet(), icmp_request_packet()]
}

View File

@@ -5,13 +5,13 @@ use proptest::arbitrary::any;
use proptest::prop_oneof;
use proptest::strategy::Strategy;
use crate::{build, MutableIpPacket};
use crate::{build, IpPacket};
use etherparse::{Ipv4Extensions, Ipv4Header, Ipv4Options, PacketBuilder};
use proptest::prelude::Just;
const EMPTY_PAYLOAD: &[u8] = &[];
fn tcp_packet_v4() -> impl Strategy<Value = MutableIpPacket<'static>> {
fn tcp_packet_v4() -> impl Strategy<Value = IpPacket<'static>> {
(
any::<Ipv4Addr>(),
any::<Ipv4Addr>(),
@@ -27,7 +27,7 @@ fn tcp_packet_v4() -> impl Strategy<Value = MutableIpPacket<'static>> {
})
}
fn tcp_packet_v6() -> impl Strategy<Value = MutableIpPacket<'static>> {
fn tcp_packet_v6() -> impl Strategy<Value = IpPacket<'static>> {
(
any::<Ipv6Addr>(),
any::<Ipv6Addr>(),
@@ -43,7 +43,7 @@ fn tcp_packet_v6() -> impl Strategy<Value = MutableIpPacket<'static>> {
})
}
fn udp_packet_v4() -> impl Strategy<Value = MutableIpPacket<'static>> {
fn udp_packet_v4() -> impl Strategy<Value = IpPacket<'static>> {
(
any::<Ipv4Addr>(),
any::<Ipv4Addr>(),
@@ -59,7 +59,7 @@ fn udp_packet_v4() -> impl Strategy<Value = MutableIpPacket<'static>> {
})
}
fn udp_packet_v6() -> impl Strategy<Value = MutableIpPacket<'static>> {
fn udp_packet_v6() -> impl Strategy<Value = IpPacket<'static>> {
(
any::<Ipv6Addr>(),
any::<Ipv6Addr>(),
@@ -75,7 +75,7 @@ fn udp_packet_v6() -> impl Strategy<Value = MutableIpPacket<'static>> {
})
}
fn icmp_request_packet_v4() -> impl Strategy<Value = MutableIpPacket<'static>> {
fn icmp_request_packet_v4() -> impl Strategy<Value = IpPacket<'static>> {
(
any::<Ipv4Addr>(),
any::<Ipv4Addr>(),
@@ -99,7 +99,7 @@ fn icmp_request_packet_v4() -> impl Strategy<Value = MutableIpPacket<'static>> {
})
}
fn icmp_reply_packet_v4() -> impl Strategy<Value = MutableIpPacket<'static>> {
fn icmp_reply_packet_v4() -> impl Strategy<Value = IpPacket<'static>> {
(
any::<Ipv4Addr>(),
any::<Ipv4Addr>(),
@@ -123,7 +123,7 @@ fn icmp_reply_packet_v4() -> impl Strategy<Value = MutableIpPacket<'static>> {
})
}
fn icmp_request_packet_v6() -> impl Strategy<Value = MutableIpPacket<'static>> {
fn icmp_request_packet_v6() -> impl Strategy<Value = IpPacket<'static>> {
(
any::<Ipv6Addr>(),
any::<Ipv6Addr>(),
@@ -138,7 +138,7 @@ fn icmp_request_packet_v6() -> impl Strategy<Value = MutableIpPacket<'static>> {
})
}
fn icmp_reply_packet_v6() -> impl Strategy<Value = MutableIpPacket<'static>> {
fn icmp_reply_packet_v6() -> impl Strategy<Value = IpPacket<'static>> {
(
any::<Ipv6Addr>(),
any::<Ipv6Addr>(),
@@ -169,7 +169,7 @@ fn ipv4_options() -> impl Strategy<Value = Ipv4Options> {
]
}
fn packet_v4() -> impl Strategy<Value = MutableIpPacket<'static>> {
fn packet_v4() -> impl Strategy<Value = IpPacket<'static>> {
prop_oneof![
tcp_packet_v4(),
udp_packet_v4(),
@@ -178,7 +178,7 @@ fn packet_v4() -> impl Strategy<Value = MutableIpPacket<'static>> {
]
}
fn packet_v6() -> impl Strategy<Value = MutableIpPacket<'static>> {
fn packet_v6() -> impl Strategy<Value = IpPacket<'static>> {
prop_oneof![
tcp_packet_v6(),
udp_packet_v6(),
@@ -189,11 +189,11 @@ fn packet_v6() -> impl Strategy<Value = MutableIpPacket<'static>> {
#[test_strategy::proptest()]
fn nat_6446(
#[strategy(packet_v6())] packet_v6: MutableIpPacket<'static>,
#[strategy(packet_v6())] packet_v6: IpPacket<'static>,
#[strategy(any::<Ipv4Addr>())] new_src: Ipv4Addr,
#[strategy(any::<Ipv4Addr>())] new_dst: Ipv4Addr,
) {
let header = packet_v6.as_immutable().ipv6_header().unwrap();
let header = packet_v6.ipv6_header().unwrap();
let payload = packet_v6.payload().to_vec();
let packet_v4 = packet_v6.consume_to_ipv4(new_src, new_dst).unwrap();
@@ -206,17 +206,17 @@ fn nat_6446(
.unwrap();
new_packet_v6.update_checksum();
assert_eq!(new_packet_v6.as_immutable().ipv6_header().unwrap(), header);
assert_eq!(new_packet_v6.ipv6_header().unwrap(), header);
assert_eq!(new_packet_v6.payload(), payload);
}
#[test_strategy::proptest()]
fn nat_4664(
#[strategy(packet_v4())] packet_v4: MutableIpPacket<'static>,
#[strategy(packet_v4())] packet_v4: IpPacket<'static>,
#[strategy(any::<Ipv6Addr>())] new_src: Ipv6Addr,
#[strategy(any::<Ipv6Addr>())] new_dst: Ipv6Addr,
) {
let header = packet_v4.as_immutable().ipv4_header().unwrap();
let header = packet_v4.ipv4_header().unwrap();
let payload = packet_v4.payload().to_vec();
let packet_v6 = packet_v4.consume_to_ipv6(new_src, new_dst).unwrap();
@@ -236,9 +236,6 @@ fn nat_4664(
};
header_without_options.header_checksum = header_without_options.calc_header_checksum();
assert_eq!(
new_packet_v4.as_immutable().ipv4_header().unwrap(),
header_without_options
);
assert_eq!(new_packet_v4.ipv4_header().unwrap(), header_without_options);
assert_eq!(new_packet_v4.payload(), payload);
}