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
- Verify the interface actually has a 6-byte (EUI-48) or 8-byte (EUI-64) hardware address: ip link show <iface>
- Point the RA/DHCPv6 config at a real Ethernet/WLAN interface, not lo or a tunnel
- 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
- Don't enable RA on tunnels/loopback/veth interfaces without hardware addresses
- Check iface.HardwareAddr length before enabling DHCPv6
- Log the MAC at startup for quick diagnosis
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
- dhcpv6 ra: icmp.ListenPacket: %w
- creating packet: %w
- resetting dhcpv6 leases: %w
- creating dhcpv6 srv: %w
- bad dhcpv6 configuration: %w
AI-assisted analysis of AdguardTeam/AdGuardHome@b41aefbe51 (2026-08-27).
Data as JSON: /api/errors/2b7ca78b0277ba5a.
Report an issue: GitHub.