txthinking/brook · error

no ipv4 from nic

Error message

no ipv4 from nic

What it means

In the same IP method, when v46 == "4" the code looks for a globally-unicast, PRIVATE IPv4 address (IsPrivate() && To4() != nil) to use as the source. Error [73] means the interface's address list contained no matching private IPv4 address — the host has no private IPv4 address on the selected NIC. The library intentionally wants an RFC1918-style source address and refuses to continue without one.

Source

Thrown at plugins/dialwithnic/dialwithnic.go:62

		for _, v := range addrs {
			if v.(*net.IPNet).IP.IsGlobalUnicast() && v.(*net.IPNet).IP.To4() == nil {
				return v.(*net.IPNet).IP, nil
			}
		}
		return nil, errors.New("no ipv6 from nic")
	}
	if v46 == "4" {
		for _, v := range addrs {
			if v.(*net.IPNet).IP.IsGlobalUnicast() && !v.(*net.IPNet).IP.IsPrivate() && v.(*net.IPNet).IP.To4() != nil {
				return v.(*net.IPNet).IP, nil
			}
		}
		for _, v := range addrs {
			if v.(*net.IPNet).IP.IsGlobalUnicast() && v.(*net.IPNet).IP.IsPrivate() && v.(*net.IPNet).IP.To4() != nil {
				return v.(*net.IPNet).IP, nil
			}
		}
		return nil, errors.New("no ipv4 from nic")
	}
	return nil, errors.New("black hole")
}

func (p *DialWithNIC) TouchBrook() {
	brook.DialTCP = func(network string, laddr, raddr string) (net.Conn, error) {
		var la, ra *net.TCPAddr
		if laddr != "" {
			var err error
			la, err = net.ResolveTCPAddr(network, laddr)
			if err != nil {
				return nil, err
			}
		}
		a, err := brook.Resolve(network, raddr)
		if err != nil {
			return nil, err
		}

View on GitHub (pinned to 5cd13ef3b1)

Solutions

  1. Verify the NIC actually has a private IPv4 address (`ip -4 addr`); if not, add/configure one or pick a different interface whose addr list is passed to IP().
  2. Check the addr-selection/filtering upstream of this loop — if the library lets you choose the NIC, select one with an RFC1918 address.
  3. If a public IPv4 is the only option and fits your use case, bypass the NIC-source constraint and use the default dialer instead of DialWithNIC.
  4. Handle the error as 'no private IPv4 available' and fall back to IPv6 or the default dial path.

Example fix

// before
ip, err := plugin.IP("4") // NIC has only a public IPv4
// after
ip, err := plugin.IP("4")
if err != nil {
    // use default dialer without binding to a private source IP
    conn, err := net.Dial("tcp", addr)
}
Defensive patterns

Strategy: fallback

Validate before calling

func hasPrivateIPv4() bool {
    for _, a := range interfaceAddrs() {
        if ipnet, ok := a.(*net.IPNet); ok && ipnet.IP.IsGlobalUnicast() && ipnet.IP.IsPrivate() && ipnet.IP.To4() != nil {
            return true
        }
    }
    return false
}
// only call plugin.IP("4") if hasPrivateIPv4()

Type guard

func isPrivateIPv4(ip net.IP) bool {
    return ip.To4() != nil && ip.IsGlobalUnicast() && ip.IsPrivate()
}

Try / catch

ip, err := plugin.IP("4")
if err != nil && err.Error() == "no ipv4 from nic" {
    log.Println("no private IPv4 on NIC, using default dialer")
    conn, err = net.Dial("tcp", addr)
}

Prevention

When it happens

Trigger: Calling IP(v46="4") when the enumerated addrs contain no global-unicast private IPv4 address — e.g. the NIC only has IPv6, only a public IPv4 address, or only loopback (127.0.0.1 fails IsGlobalUnicast for the intended use).

Common situations: Running in environments where the NIC has only a public IP (some cloud VMs with public IPs directly on the interface); IPv6-only containers; misconfigured interface selection passing the wrong interface's addr list; hosts where the only IPv4 address is loopback.

Understand the failure class

Background: 'Could not be found', 'does not exist', 'not found in database': the resource-not-found family when an ID, slug, key, or URI lookup comes back empty — this error's family across 20 libraries.

Related errors


AI-assisted analysis of txthinking/brook@5cd13ef3b1 (2026-09-06). Data as JSON: /api/errors/db23da24a599194c. Report an issue: GitHub.