Why Your Kubernetes Pod Using macvlan Can't Talk to the Host

Why Your Kubernetes Pod Using macvlan Can't Talk to the Host

Understanding Host ↔ Pod Communication Issues with Macvlan in Kubernetes

If you’ve implemented macvlan interfaces in a Kubernetes environment and you’re puzzled because your pod cannot communicate with the host—even though they’re on the same subnet—you are definitely not alone. This is a well-known behavior due to how the Linux kernel handles macvlan interfaces. In this post, we’ll go through:

  • What macvlan is and when you might use it
  • Why communication between the host and a pod on a macvlan interface typically fails
  • What exactly is happening in the kernel under the hood
  • Practical workarounds to enable or bypass host-to-pod communication

What is macvlan?

macvlan is a Linux virtual network interface driver that allows containers or pods to appear as independent network devices on a physical interface. Key features:

  • Each macvlan interface gets its own unique MAC and IP address.
  • It is attached to a parent physical interface (e.g., eth0 on the host).
  • It allows pods/containers to have direct L2 access to the host’s network—so they share the same subnet as the host or other networked devices.

This makes macvlan appealing in scenarios where each pod needs its own identity on the local network, rather than being NATed behind the host.


The Problem: Host Can’t Talk to Pod

Consider this setup:

  • The host’s NIC (e.g., eth0) is the parent of a macvlan interface.
  • A pod has a macvlan interface (e.g., macvlan0) and an IP in the same subnet as the host.
  • The host itself holds an IP in that same subnet (on eth0 or another interface).

What works:

  • The pod can talk to external systems
  • External systems can talk to the pod
  • The host can talk to external systems

What doesn’t work:

  • The host cannot reliably talk to the pod

It may appear as though everything is configured correctly (same subnet, MAC and IP assigned, etc.). But the host-to-pod communication fails because of the way macvlan is implemented in the kernel.


Where It Breaks: Inside the Kernel

How macvlan works under the hood

When you create a macvlan interface, you’re effectively creating a new net_device in the Linux kernel:

  • It gets its own MAC address and IP address.
  • It is associated with a parent “lower” device (such as eth0).
  • The kernel attaches an RX handler on the lower device so that when frames arrive via the physical NIC, they can be handed off to the macvlan infrastructure (function: macvlan_handle_frame() in drivers/net/macvlan.c).
  • On transmit, a macvlan device typically sends via the lower device (function: macvlan_queue_xmit() / macvlan_start_xmit()), setting skb->dev = lowerdev and calling the child’s transmit logic, which goes out via the physical NIC.

Why Host ↔ Pod Fails

Here’s the crux: When the host sends a packet to the pod’s MAC/IP on the same physical interface, that packet leaves via the lower device (transmit path) but it does not traverse the RX handler path that the macvlan infrastructure uses for incoming frames.

  • Locally generated packets on the host bypass the lower device’s receive path and thus skip the macvlan_handle_frame() logic.
  • macvlan_handle_frame() only processes frames that arrive on the lower device’s receive side (i.e., frames that came in from the wire).
  • Because of this, packets from the host to the pod are never delivered via the macvlan RX logic—the pod never sees them.
  • The isolation is not due to a firewall rule or explicit “deny host to macvlan” code; it’s simply how the network stack is structured: local transmit does not hand back into local receive.

Broadcast and multicast behave similarly: although the macvlan infrastructure can handle broadcast/multicast fan-out among macvlan interfaces, a broadcast sent locally by the host on the lower device still may not enter the RX handler chain for macvlan peers unless it arrives on the lowerdev from the wire.


Workarounds (If You Need Host ↔ Pod Communication)

If you want a setup where:

  • Your pod uses a macvlan interface
  • The pod is on the same subnet as the host and external devices
  • You need host ↔ pod communication (in addition to pod ↔ external)

Then you can pick one of the following approaches:

Option 1: Use an OVS Bridge + Internal Port for the Host

  • Create a bridge (e.g., br0) and add the physical NIC (eth0) to it.
  • Add an internal port on the bridge (e.g., host0) and assign the host’s IP to that internal port.
  • Why this works: the host now behaves as a “peer” on the bridge (like a separate interface) rather than as the lower device itself; the macvlan logic can deliver packets to that peer.

Attach macvlan interfaces (or containers/pods) to the same bridge segment.

ovs-vsctl add-br br0
ovs-vsctl add-port br0 eth0
ovs-vsctl add-port br0 host0 -- set Interface host0 type=internal
ip link set dev host0 up
ip addr add 192.168.0.10/24 dev host0

Option 2: Use a Linux Bridge + veth Pair

  • Create a Linux bridge (e.g., br0) and add eth0.
  • Create a veth pair: veth-host <-> veth-br. Attach veth-br to br0, assign the host IP to veth-host.
  • Containers/pods join br0. The host becomes a peer on the bridge via the veth-host.
  • This setup ensures the host and pods are on the same L2 domain and can exchange frames locally.

Option 3: Use a Dedicated VLAN or Subnet + Routing

  • Instead of forcing host↔pod on the same L2 with macvlan, assign the pod(s) an IP in a different subnet or VLAN.
  • Configure routing (either on the host or upstream) so the host ↔ pod traffic goes via routed path.
  • This avoids relying on macvlan’s L2 delivery entirely.

What Doesn't Work: Assign Host IP to br0 or eth0 and Expect Seamless Delivery

  • Simply assigning the host’s IP to the bridge (br0) or parent interface (eth0) while pods are on macvlan doesn’t make the host a true “peer” on the L2 domain.
  • Local packets from the host still don’t traverse the macvlan RX handler, so they will not reach the pod’s macvlan device.

TL;DR

  • macvlan gives each pod or container its own MAC & IP on the host’s physical interface.
  • Host-to-macvlan communication typically fails because locally transmitted packets bypass the receive path where macvlan delivers frames.
  • To enable host↔pod traffic, treat the host as a peer interface (via bridge, veth, internal port) rather than as the lower device itself.
  • Alternatively, use routing instead of L2-peer sharing if the host and pod must communicate but you want to preserve isolation.

References

  • macvlan documentation in the Linux kernel (drivers/net/macvlan.c)
  • Linux Kernel Networking API docs: RX handler contract (net/core/dev.c)
  • Kernel source of macvlan_handle_frame() logic (macvlan RX fan-out)
  • Kernel source of macvlan_queue_xmit() / macvlan_start_xmit() logic (transmit path)