AdguardTeam/AdGuardHome · error

converting source link-layer address: %w

Error message

converting source link-layer address: %w

What it means

Raised in createICMPv6RAPacket when the configured source link-layer address (MAC) cannot be converted into the NDP source link-layer address option format. The wrapped hwAddrToLinkLayerAddr error fires when the hardware address is nil, the wrong length (not 6 or 8 bytes), or otherwise unsuitable for an ICMPv6 RA.

Source

Thrown at internal/dhcpd/routeradv.go:128

//	  - Option=MTU(5):
//	    - Type[1]
//	    - Length * 8bytes[1]
//	    - Reserved[2]
//	    - MTU[4]
//	  - Option=Source link-layer address(1):
//	    - Link-Layer Address[8/24]
//	  - Option=Recursive DNS Server(25):
//	    - Type[1]
//	    - Length * 8bytes[1]
//	    - Reserved[2]
//	    - Lifetime[4]
//	    - Addresses of IPv6 Recursive DNS Servers[16]
//
// TODO(a.garipov): Replace with an existing implementation from a dependency.
func createICMPv6RAPacket(params icmpv6RA) (data []byte, err error) {
	lla, err := hwAddrToLinkLayerAddr(params.sourceLinkLayerAddress)
	if err != nil {
		return nil, fmt.Errorf("converting source link-layer address: %w", err)
	}

	// Calculate length of the source link-layer address option.  As per RFC
	// 4861, section 4.6.1, the length should be in units of 8 octets, including
	// the type and length fields.
	//
	// See https://datatracker.ietf.org/doc/html/rfc4861#section-4.6.1.
	srcLLAOptLen := len(lla) + 2
	// Make sure the value is rounded up to the nearest multiple of 8.
	srcLLAOptLenValue := (srcLLAOptLen + 7) / 8
	srcLLAPadLen := srcLLAOptLenValue*8 - srcLLAOptLen

	// TODO(a.garipov): Don't use a magic constant here.  Refactor the code
	// and make all constants named instead of all those comments.
	data = make([]byte, 80+srcLLAOptLen+srcLLAPadLen)
	i := 0

	// ICMPv6:

View on GitHub (pinned to b41aefbe51)

Solutions

  1. Verify the interface actually has a 6-byte (EUI-48) or 8-byte (EUI-64) hardware address: ip link show <iface>
  2. Point the RA/DHCPv6 config at a real Ethernet/WLAN interface, not lo or a tunnel
  3. Log iface.HardwareAddr length and value at startup to catch odd virtual NICs early
Defensive patterns

Strategy: validation

Validate before calling

iface, err := net.InterfaceByName(name)
if err != nil || len(iface.HardwareAddr) != 6 && len(iface.HardwareAddr) != 8 {
    return fmt.Errorf("interface %s has no usable MAC", name)
}

Type guard

func hasUsableMAC(iface *net.Interface) bool {
	n := len(iface.HardwareAddr)
	return n == 6 || n == 8
}

Prevention

When it happens

Trigger: Building a router-advertisement packet where the interface's MAC address is unavailable (pure L3 interfaces, tunnels, some containers) or malformed; unit tests passing a zero-value MAC. Hit during RA Init or tests exercising createICMPv6RAPacket.

Common situations: Enabling DHCPv6/RA on tun/tap or VLAN-only interfaces with no hardware address; virtualized NICs reporting unusual MAC lengths; config referencing an interface index that resolves to a loopback.

Related errors


AI-assisted analysis of AdguardTeam/AdGuardHome@b41aefbe51 (2026-08-27). Data as JSON: /api/errors/2b7ca78b0277ba5a. Report an issue: GitHub.