cilium/cilium · error

interface %s egress: %w

Error message

interface %s egress: %w

What it means

Same family as the ingress failure but for the egress direction: attachSKBProgram failed attaching cil_from_host to cilium_host egress (HANDLE_MIN_EGRESS), wrapped as 'interface %s egress'. It indicates outbound-from-host traffic will not traverse the Cilium BPF programs, breaking host-originated traffic handling. Inner causes mirror the ingress case (qdisc, tcx, pinning, program load).

Source

Thrown at pkg/datapath/loader/host.go:137

		return err
	}
	defer cleanup()
	defer hostObj.Close()

	// Insert host endpoint policy program.
	if err := hostObj.PolicyMap.Update(uint32(ep.GetID()), hostObj.PolicyProg, ebpf.UpdateAny); err != nil {
		return fmt.Errorf("inserting host endpoint policy program: %w", err)
	}

	// Attach cil_to_host to cilium_host ingress.
	if err := attachSKBProgram(logger, host, hostObj.ToHost, symbolToHostEp,
		bpffsDeviceLinksDir(bpf.CiliumPath(), host), netlink.HANDLE_MIN_INGRESS, option.Config.EnableTCX); err != nil {
		return fmt.Errorf("interface %s ingress: %w", ep.InterfaceName(), err)
	}
	// Attach cil_from_host to cilium_host egress.
	if err := attachSKBProgram(logger, host, hostObj.FromHost, symbolFromHostEp,
		bpffsDeviceLinksDir(bpf.CiliumPath(), host), netlink.HANDLE_MIN_EGRESS, option.Config.EnableTCX); err != nil {
		return fmt.Errorf("interface %s egress: %w", ep.InterfaceName(), err)
	}

	if err := commit(); err != nil {
		return fmt.Errorf("committing bpf pins: %w", err)
	}

	return nil
}

// ciliumNetConfigs holds functions that yield a BPF configuration object for
// cilium_net.
var ciliumNetConfigs funcRegistry[func(endpoint.Config, *config.Config, netlink.Link) any]

// ciliumNetRenames holds functions that yield BPF map renames for cilium_net.
var ciliumNetRenames funcRegistry[func(endpoint.Config, *config.Config, netlink.Link) map[string]string]

// ciliumNetConfiguration returns a slice of BPF configuration objects yielded
// by all registered config providers of [ciliumNetConfigs].

View on GitHub (pinned to ac7b90affa)

Solutions

  1. Check `tc filter show dev cilium_host egress` for stale or conflicting filters; `tc qdisc del dev cilium_host clsact` and let the agent recreate it.
  2. Verify kernel support for the chosen attach mode; disable --enable-tcx on kernels < 6.6.
  3. Clear stale egress link pins under the device's bpffs links dir and restart the agent for a clean attach.
  4. Confirm CAP_NET_ADMIN/CAP_BPF privileges for egress qdisc and link operations.
  5. Inspect errors.Is/Unwrap on the wrapped error to distinguish EEXIST (stale pin) from EPERM (permissions) before cleanup.

Example fix

// before: reload leaves half-attached device after egress failure
// agent logs: attaching cilium_host: interface cilium_host egress: ...

// after: clean device hooks before retrying reload
exec.Command("tc", "qdisc", "del", "dev", "cilium_host", "clsact").Run()
os.RemoveAll(bpffsDeviceLinksDir("/sys/fs/bpf/cilium", host))
// then restart cilium-agent to re-run reloadHostEndpoint
Defensive patterns

Strategy: retry

Validate before calling

// Pre-check both hooks before reload to avoid half-attached state
func canAttachBothDirections(dev string, enableTCX bool) error {
    if enableTCX && !kernelHasTCX() {
        return errors.New("tcx egress unsupported on this kernel")
    }
    filters, err := safenetlink.FilterList(nil, netlink.MakeHandle(0xffff, 0))
    if err != nil {
        return fmt.Errorf("cannot inspect egress filters: %w", err)
    }
    _ = filters
    return nil
}

Type guard

func isEgressAttachError(err error) bool {
    return strings.Contains(err.Error(), "egress") &&
        (errors.Is(err, unix.EEXIST) || errors.Is(err, unix.EPERM) ||
         errors.Is(err, unix.ENOSYS))
}

Try / catch

if err := reload(); err != nil {
    if isEgressAttachError(err) {
        // clean egress hook and retry with backoff
        detachEgress("cilium_host")
        err = retryWithBackoff(reload, 3)
    }
    if err != nil {
        // last resort: full datapath resync
        triggerFullDatapathResync()
    }
}

Prevention

When it happens

Trigger: attachCiliumHost's second attachSKBProgram call (hostObj.FromHost, symbolFromHostEp, netlink.HANDLE_MIN_EGRESS, option.Config.EnableTCX) errors after ingress succeeded: egress hook unsupported, tcx egress link creation fails, bpffs pin conflict for the egress link path, or EPERM on qdisc operation.

Common situations: Half-attached state after a partial failure (ingress OK, egress fails) leaving stale ingress links to clean; kernel without egress tc support combined with --enable-tcx; pinned egress links left from a previous agent version under /sys/fs/bpf/cilium; netlink qdisc churn during node network reconfiguration.

Related errors


AI-assisted analysis of cilium/cilium@ac7b90affa (2026-08-31). Data as JSON: /api/errors/507d238aa97f8e76. Report an issue: GitHub.