cilium/cilium · error

route to destination %s contains gateway %s, must be directl

Error message

route to destination %s contains gateway %s, must be directly reachable. Add `direct-routing-skip-unreachable` to skip unreachable routes

What it means

When building a direct route, createDirectRouteSpec requires the kernel route to the peer node IP to be directly connected (no gateway), because Cilium would otherwise install a route spec pointing at the wrong next-hop. If routes[0].Gw is set (non-nil, non-unspecified, and different from the node IP itself) and skip-unreachable is not enabled, it returns this error telling the operator the route is gatewayed and how to skip such routes.

Source

Thrown at pkg/datapath/linux/node.go:191

	routes, err = netlink.RouteGet(nodeIP)
	if err != nil {
		err = fmt.Errorf("unable to lookup route for node %s: %w", nodeIP, err)
		return
	}

	if len(routes) == 0 {
		err = fmt.Errorf("no route found to destination %s", nodeIP.String())
		return
	}

	if routes[0].Gw != nil && !routes[0].Gw.IsUnspecified() && !routes[0].Gw.Equal(nodeIP) {
		if skipUnreachable {
			log.Debug("route to destination contains gateway, skipping route as not directly reachable",
				logfields.NodeIP, nodeIP,
				logfields.GatewayIP, routes[0].Gw)
			addRoute = false
		} else {
			err = fmt.Errorf("route to destination %s contains gateway %s, must be directly reachable. Add `direct-routing-skip-unreachable` to skip unreachable routes",
				nodeIP, routes[0].Gw.String())
		}
		return
	}

	linkIndex := routes[0].LinkIndex

	// Special treatment if the route points to the loopback, lookup the
	// local route and use that ifindex
	if linkIndex == 1 {
		family := netlink.FAMILY_V4
		dst := &net.IPNet{IP: nodeIP, Mask: net.CIDRMask(32, 32)}
		if nodeIP.To4() == nil {
			family = netlink.FAMILY_V6
			dst.Mask = net.CIDRMask(128, 128)
		}

		filter := &netlink.Route{

View on GitHub (pinned to ac7b90affa)

Solutions

  1. Add --direct-routing-skip-unreachable=true (helm: --set directRoutingSkipUnreachable=true) so gatewayed (non-directly-reachable) node routes are skipped instead of failing
  2. Make peer nodes directly reachable at L2: put nodes on the same subnet/VLAN, or set up direct routes without a gateway
  3. Switch back to encapsulation (tunnel mode, e.g. vxlan/geneve) if nodes are genuinely routed through a gateway — direct routing is not the right mode for this topology
  4. Verify the host route with `ip route get <nodeIP>`; if a gateway is expected, skip-unreachable is the correct configuration

Example fix

// before: direct routing across a gatewayed route fails
# cilium-agent --direct-routing=true --auto-direct-node-routes=true
// after: skip gatewayed routes
# cilium-agent --direct-routing=true --auto-direct-node-routes=true --direct-routing-skip-unreachable=true
Defensive patterns

Strategy: validation

Validate before calling

// detect gatewayed routes to peer nodes before enabling direct routing
const { execSync } = require('child_process');
function routeIsDirect(ip) {
  const out = execSync(`ip route get ${ip}`).toString();
  return !/\bvia\s+/.test(out); // a 'via X' means gatewayed, not directly connected
}
if (!routeIsDirect('10.0.2.15')) {
  // enable --direct-routing-skip-unreachable or switch to tunnel mode
  console.warn('peer node reachable only via gateway; direct routing will fail');
}

Type guard

function isDirectlyConnected(routeGetOutput) {
  return typeof routeGetOutput === 'string' && !/\bvia\s+\S+/.test(routeGetOutput);
}

Try / catch

try {
  await installDirectRoute(nodeIP, linkIndex);
} catch (err) {
  if (/contains gateway/.test(err.message)) {
    log.warn('gatewayed node route skipped; enable direct-routing-skip-unreachable or use tunneling', { nodeIP });
    return skipRoute(nodeIP);
  }
  throw err;
}

Prevention

When it happens

Trigger: installDirectRoute -> createDirectRouteSpec with native/direct routing where `ip route get <nodeIP>` resolves via a gateway (e.g. 'via 10.0.0.1'), and the agent was NOT started with --direct-routing-skip-unreachable=true.

Common situations: Nodes on different L2 segments or subnets while autoDirectNodeRoutes/direct routing expects L2 adjacency; cloud environments where inter-node traffic traverses a router (VPC gateway) rather than being directly connected; VPN/WireGuard or SDN setups that present a gatewayed route to peer nodes; migrating from overlay mode to native routing without checking topology.

Related errors


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