stamparm/maltrail · error

src ipv6

Error message

src ipv6

What it means

This is a test-kit invariant panic, not a catchable error: the ipv6() packet builder in sensor/src/testkit.rs converts its src address string via addr_to_int() and unwraps with expect("src ipv6"), which fires when the caller passes a src string that is not a valid IPv6 address.ipv6() is a test fixture used by ipv6_detections, ipv6_addr_port_trail, seeds, and valid_seeds to synthesize IPv6 packet bytes, so a malformed src means a broken test seed/address literal, never production input. Fix by correcting the src literal in the calling test to a parseable IPv6 address (e.g. '2001:db8::1').

Solutions

  1. Validate the src string with parse_ipv6 or std's Ipv6Addr::from_str before building the packet
  2. Use canonical literals such as "2001:db8::1"
  3. Match on parse_ipv6 to include the offending string in the panic message
  4. Ensure the IPv6 builder is not accidentally given IPv4 literals

Example fix

// before
ipv6(proto, "2001:db8:::1", dst, payload) // malformed compression
// after
ipv6(proto, "2001:db8::1", dst, payload)
Defensive patterns

Strategy: validation

Validate before calling

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

Type guard

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

Try / catch

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

Prevention

When it happens

Trigger: `ipv6()` called with a src string that is not a valid IPv6 literal (empty, IPv4-only, bad group count, invalid hex, misuse of :: compression).

Common situations: Typo in hardcoded test IPv6 like "2001:db8::1"; passing an IPv4 address to the IPv6 builder; string interpolation producing unbalanced colons.

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/e0fd6d15a93218ad. Report an issue: GitHub.

Appendix: source

Thrown at sensor/src/testkit.rs:462

    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`.
///
/// 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)