stamparm/maltrail · error

dst ipv6

Error message

dst ipv6

What it means

Same test-kit invariant as its src counterpart: in the ipv6() builder the destination address is converted with addr_to_int(dst).expect("dst ipv6"), which panics when dst is not a valid IPv6 address string. Since ipv6() is only a fixture synthesizing packet bytes for tests (ipv6_detections, ipv6_addr_port_trail, seeds, valid_seeds), the panic indicates an invalid IPv6 literal passed as dst by a test, not a runtime condition. Fix by supplying a well-formed IPv6 destination address in the calling test code.

Solutions

  1. Validate dst parses as IPv6 before calling the builder
  2. Correct the literal to a canonical IPv6 form
  3. Match on parse_ipv6 to surface the invalid value in the panic
  4. Keep src/dst argument order consistent to avoid passing an IPv4 where IPv6 is expected

Example fix

// before
ipv6(proto, src, "fe80::1::2", payload) // double compression
// after
ipv6(proto, src, "fe80::2", payload)
Defensive patterns

Strategy: validation

Validate before calling

fn is_ipv6(s: &str) -> bool { s.parse::<std::net::Ipv6Addr>().is_ok() }
assert!(is_ipv6(dst), "dst must be valid IPv6: {dst}");

Type guard

fn is_ipv6(s: &str) -> bool { s.parse::<std::net::Ipv6Addr>().is_ok() }

Try / catch

let v = crate::addr::parse_ipv6(dst).unwrap_or_else(|e| panic!("dst ipv6 '{dst}': {e:?}"));

Prevention

When it happens

Trigger: `ipv6()` called with a dst string that is not a valid IPv6 literal — empty string, IPv4 literal, wrong group count, invalid hex digits, or malformed :: usage.

Common situations: Hardcoded test destination with a typo; format!-generated addresses with missing groups; swapping src/dst arguments where one holds an IPv4.

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


AI-assisted analysis of stamparm/maltrail@77cfb06d76 (2026-09-13). Data as JSON: /api/errors/5ff99b4470ffea25. Report an issue: GitHub.

Appendix: source

Thrown at sensor/src/testkit.rs:463

    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`.
///
/// Each `exts` entry is (header type, body length in bytes NOT counting the 2-byte
/// Next-Header/Hdr-Ext-Len prefix). Type 44 (Fragment) is fixed at 8 bytes total and ignores the
/// requested length, which is what makes it usable for the non-first-fragment case.
pub fn ipv6_ext(proto: u8, src: &str, dst: &str, exts: &[(u8, usize)], payload: &[u8]) -> Vec<u8> {
    let mut chain: Vec<u8> = Vec::new();
    for (i, &(kind, body)) in exts.iter().enumerate() {
        // each header names the NEXT one, and the last names the transport protocol
        let next = exts.get(i + 1).map(|&(k, _)| k).unwrap_or(proto);
        if kind == 44 {
            chain.extend_from_slice(&[next, 0, 0, 0, 0, 0, 0, 0]);
            continue;
        }

View on GitHub (pinned to 77cfb06d76)