mirror of
https://github.com/jmcorgan/fips.git
synced 2026-08-09 08:14:42 +00:00
Fix ICMPv6 Packet Too Big source address for PMTUD
The PTB source was set to the local FIPS address, which Linux ignores (loopback PTB security check). Change the source to the original packet's destination (the remote peer) so the kernel sees it as coming from a remote router and honors the PMTU update. Add test coverage for the PTB source address invariant in both the icmp unit tests and the node integration tests.
This commit is contained in:
@@ -1662,7 +1662,6 @@ impl Node {
|
||||
/// misconfigured applications sending repeated oversized packets.
|
||||
pub(in crate::node) fn send_icmpv6_packet_too_big(&mut self, original_packet: &[u8], mtu: u32) {
|
||||
use crate::upper::icmp::build_packet_too_big;
|
||||
use crate::FipsAddress;
|
||||
use std::net::Ipv6Addr;
|
||||
|
||||
// Extract source address for rate limiting
|
||||
@@ -1680,18 +1679,20 @@ impl Node {
|
||||
return;
|
||||
}
|
||||
|
||||
let our_ipv6 = FipsAddress::from_node_addr(self.node_addr()).to_ipv6();
|
||||
if let Some(response) = build_packet_too_big(original_packet, mtu, our_ipv6)
|
||||
// Use the original packet's *destination* as the ICMP source so the
|
||||
// kernel sees the PTB coming from a remote router, not from itself.
|
||||
// Linux ignores PTBs whose source matches a local address, which
|
||||
// causes a PMTUD blackhole when both src and ICMP-src are local.
|
||||
let dest_addr = Ipv6Addr::from(<[u8; 16]>::try_from(&original_packet[24..40]).unwrap());
|
||||
if let Some(response) = build_packet_too_big(original_packet, mtu, dest_addr)
|
||||
&& let Some(tun_tx) = &self.tun_tx
|
||||
{
|
||||
debug!(
|
||||
src = %src_addr,
|
||||
dst = %our_ipv6,
|
||||
original_src = %src_addr,
|
||||
original_dst = %dest_addr,
|
||||
packet_size = original_packet.len(),
|
||||
reported_mtu = mtu,
|
||||
"Sending ICMP Packet Too Big (ICMP src={}, dst={})",
|
||||
our_ipv6,
|
||||
src_addr
|
||||
"Sending ICMP Packet Too Big"
|
||||
);
|
||||
let _ = tun_tx.send(response);
|
||||
}
|
||||
|
||||
@@ -1779,6 +1779,14 @@ async fn test_tun_outbound_path_mtu_generates_ptb() {
|
||||
assert_eq!(ptb[40], 2, "ICMPv6 type should be Packet Too Big (2)");
|
||||
assert_eq!(ptb[41], 0, "ICMPv6 code should be 0");
|
||||
|
||||
// Verify PTB source is the *remote peer* (original packet's destination),
|
||||
// NOT the local node. Linux ignores PTBs whose source matches a local
|
||||
// address, causing a PMTUD blackhole.
|
||||
let ptb_src = std::net::Ipv6Addr::from(<[u8; 16]>::try_from(&ptb[8..24]).unwrap());
|
||||
let ptb_dst = std::net::Ipv6Addr::from(<[u8; 16]>::try_from(&ptb[24..40]).unwrap());
|
||||
assert_eq!(ptb_src, dst_fips.to_ipv6(), "PTB source must be remote peer (original dst), not local node");
|
||||
assert_eq!(ptb_dst, src_fips.to_ipv6(), "PTB destination must be local node (original src)");
|
||||
|
||||
// Verify reported MTU (32-bit field at ICMPv6 header bytes 4-7)
|
||||
let reported_mtu = u32::from_be_bytes([ptb[44], ptb[45], ptb[46], ptb[47]]);
|
||||
assert_eq!(reported_mtu, reduced_ipv6_mtu as u32, "Reported MTU should match path IPv6 MTU");
|
||||
@@ -1904,6 +1912,14 @@ async fn test_multihop_pmtud_heterogeneous_mtu() {
|
||||
assert_eq!(ptb[40], 2, "ICMPv6 type should be Packet Too Big (2)");
|
||||
assert_eq!(ptb[41], 0, "ICMPv6 code should be 0");
|
||||
|
||||
// Verify PTB source is the *remote peer* (original packet's destination),
|
||||
// NOT the local node. Linux ignores PTBs whose source matches a local
|
||||
// address, causing a PMTUD blackhole.
|
||||
let ptb_src = std::net::Ipv6Addr::from(<[u8; 16]>::try_from(&ptb[8..24]).unwrap());
|
||||
let ptb_dst = std::net::Ipv6Addr::from(<[u8; 16]>::try_from(&ptb[24..40]).unwrap());
|
||||
assert_eq!(ptb_src, dst_fips.to_ipv6(), "PTB source must be remote peer (original dst), not local node");
|
||||
assert_eq!(ptb_dst, src_fips.to_ipv6(), "PTB destination must be local node (original src)");
|
||||
|
||||
// Verify reported MTU is the path MTU (not local MTU)
|
||||
let reported_mtu = u32::from_be_bytes([ptb[44], ptb[45], ptb[46], ptb[47]]);
|
||||
let expected_ipv6_mtu = crate::upper::icmp::effective_ipv6_mtu(path_mtu) as u32;
|
||||
|
||||
Reference in New Issue
Block a user