stamparm/maltrail · error
dst ipv4
Error message
dst ipv4
What it means
Panic from `.expect("dst ipv4")` when `crate::addr::addr_to_int(dst)` fails to parse the destination IPv4 address in `ipv4_opts`. Same strictness as the source address check, applied to the destination.
Solutions
- Validate the dst string parses as IPv4 before calling the builder
- Replace the malformed literal with a valid dotted-quad address
- Panic with the address value via match for faster diagnosis
- Add a small helper test asserting the helper builders round-trip valid addresses
Example fix
// before ipv4_opts(proto, src, "192.168.1", &[], payload) // missing octet // after ipv4_opts(proto, src, "192.168.1.1", &[], payload)
Defensive patterns
Strategy: validation
Validate before calling
fn is_ipv4(s: &str) -> bool { s.parse::<std::net::Ipv4Addr>().is_ok() }
assert!(is_ipv4(dst), "dst must be valid IPv4: {dst}"); Type guard
fn is_ipv4(s: &str) -> bool { s.parse::<std::net::Ipv4Addr>().is_ok() } Try / catch
let v = crate::addr::addr_to_int(dst).unwrap_or_else(|e| panic!("dst ipv4 '{dst}': {e:?}")); Prevention
- Check dotted-quad completeness (four octets) in generated addresses
- Validate literals before building packets
- Keep src/dst argument order fixed to avoid swapped types
When it happens
Trigger: `ipv4_opts()`/`ipv4` called with a dst string that is not a valid dotted-quad IPv4 address (empty, hostname, out-of-range octet, IPv6 literal).
Common situations: Copy-paste of an IPv6 destination into the IPv4 builder; dynamically generated dst with a formatting bug; placeholder string left unedited.
Understand the failure class
Background: "Invalid ... format", "must be in format X", "does not look like a ..." — invalid argument format errors across CLI tools and libraries — this error's family across 17 libraries.
Related errors
- src ipv4
- src ipv6
- dst ipv6
- invalid USERS entry ' ' [?] (hint: add whitespace at start…
- invalid configuration
AI-assisted analysis of stamparm/maltrail@77cfb06d76 (2026-09-13).
Data as JSON: /api/errors/531354c9e1b4d4eb.
Report an issue: GitHub.
Appendix: source
Thrown at sensor/src/testkit.rs:451
out
}
pub fn ipv4(proto: u8, src: &str, dst: &str, payload: &[u8]) -> Vec<u8> {
ipv4_opts(proto, src, dst, payload, 5, 0)
}
pub fn ipv4_opts(proto: u8, src: &str, dst: &str, payload: &[u8], ihl: u8, frag: u16) -> Vec<u8> {
let options = vec![0u8; ((ihl as usize).saturating_sub(5)) * 4];
let total = (ihl as usize * 4 + payload.len()) as u16;
let mut out = vec![0x40 | (ihl & 0x0f), 0];
out.extend_from_slice(&total.to_be_bytes());
out.extend_from_slice(&0x1234u16.to_be_bytes());
out.extend_from_slice(&frag.to_be_bytes());
out.push(64);
out.push(proto);
out.extend_from_slice(&[0, 0]);
out.extend_from_slice(&crate::addr::addr_to_int(src).expect("src ipv4").to_be_bytes());
out.extend_from_slice(&crate::addr::addr_to_int(dst).expect("dst ipv4").to_be_bytes());
out.extend_from_slice(&options);
out.extend_from_slice(payload);
out
}
pub fn ipv6(proto: u8, src: &str, dst: &str, payload: &[u8]) -> Vec<u8> {
let mut out = vec![0x60, 0, 0, 0];
out.extend_from_slice(&(payload.len() as u16).to_be_bytes());
out.push(proto);
out.push(64);
out.extend_from_slice(&crate::addr::parse_ipv6(src).expect("src ipv6").to_be_bytes());
out.extend_from_slice(&crate::addr::parse_ipv6(dst).expect("dst ipv6").to_be_bytes());
out.extend_from_slice(payload);
out
}
/// IPv6 carrying a chain of extension headers before `proto`.
///View on GitHub (pinned to 77cfb06d76)