ironcore-dev / ironcore-dev/dpservice

VM cannot reach its own VIP: hairpin packet is DNATed but not SNATed (`src == dst`)

Open
#815 0 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

area/networking
Dominant language
C
Stars
17
Forks
10
Avg merge
1d 6h
Merged PRs (30d)
5

Description

Describe the bug

A VM with a VirtualIP cannot reach its own VIP. dpservice rewrites the destination (VIP → private IP) and sends the packet back to the same VM, but keeps the private source IP. The VM receives 10.0.0.10 > 10.0.0.10 and drops it. ICMP and TCP both fail.

Use case: a service and its client run on the same VM, and DNS points to the VIP (NAT loopback).

To Reproduce

VM 10.0.0.10 with VIP 194.11.242.75 (egress is SNATed to the VIP), network with a public NATGateway 194.11.242.60.

$ ping -c 5 194.11.242.75
5 packets transmitted, 0 received, 100% packet loss, time 4091ms

$ tcpdump -ni enp5s0 -e -v icmp
15:36:08.060263 b6:1c:3e:a5:ce:17 > fe:7d:a0:0c:fe:c5, ethertype IPv4 (0x0800), length 98: (tos 0x0, ttl 64, id 40485, offset 0, flags [DF], proto ICMP (1), length 84)
    10.0.0.10 > 194.11.242.75: ICMP echo request, id 21, seq 1, length 64
15:36:08.060500 fe:7d:a0:0c:fe:c5 > b6:1c:3e:a5:ce:17, ethertype IPv4 (0x0800), length 98: (tos 0x0, ttl 64, id 40485, offset 0, flags [DF], proto ICMP (1), length 84)
    10.0.0.10 > 10.0.0.10: ICMP echo request, id 21, seq 1, length 64
15:36:09.075440 b6:1c:3e:a5:ce:17 > fe:7d:a0:0c:fe:c5, ethertype IPv4 (0x0800), length 98: (tos 0x0, ttl 64, id 40565, offset 0, flags [DF], proto ICMP (1), length 84)
    10.0.0.10 > 194.11.242.75: ICMP echo request, id 21, seq 2, length 64
15:36:09.075559 fe:7d:a0:0c:fe:c5 > b6:1c:3e:a5:ce:17, ethertype IPv4 (0x0800), length 98: (tos 0x0, ttl 64, id 40565, offset 0, flags [DF], proto ICMP (1), length 84)
    10.0.0.10 > 10.0.0.10: ICMP echo request, id 21, seq 2, length 64
15:36:10.099554 b6:1c:3e:a5:ce:17 > fe:7d:a0:0c:fe:c5, ethertype IPv4 (0x0800), length 98: (tos 0x0, ttl 64, id 40714, offset 0, flags [DF], proto ICMP (1), length 84)
    10.0.0.10 > 194.11.242.75: ICMP echo request, id 21, seq 3, length 64
15:36:10.099787 fe:7d:a0:0c:fe:c5 > b6:1c:3e:a5:ce:17, ethertype IPv4 (0x0800), length 98: (tos 0x0, ttl 64, id 40714, offset 0, flags [DF], proto ICMP (1), length 84)
    10.0.0.10 > 10.0.0.10: ICMP echo request, id 21, seq 3, length 64
15:36:11.123466 b6:1c:3e:a5:ce:17 > fe:7d:a0:0c:fe:c5, ethertype IPv4 (0x0800), length 98: (tos 0x0, ttl 64, id 40783, offset 0, flags [DF], proto ICMP (1), length 84)
    10.0.0.10 > 194.11.242.75: ICMP echo request, id 21, seq 4, length 64
15:36:11.123574 fe:7d:a0:0c:fe:c5 > b6:1c:3e:a5:ce:17, ethertype IPv4 (0x0800), length 98: (tos 0x0, ttl 64, id 40783, offset 0, flags [DF], proto ICMP (1), length 84)
    10.0.0.10 > 10.0.0.10: ICMP echo request, id 21, seq 4, length 64
15:36:12.151388 b6:1c:3e:a5:ce:17 > fe:7d:a0:0c:fe:c5, ethertype IPv4 (0x0800), length 98: (tos 0x0, ttl 64, id 40944, offset 0, flags [DF], proto ICMP (1), length 84)
    10.0.0.10 > 194.11.242.75: ICMP echo request, id 21, seq 5, length 64
15:36:12.151657 fe:7d:a0:0c:fe:c5 > b6:1c:3e:a5:ce:17, ethertype IPv4 (0x0800), length 98: (tos 0x0, ttl 64, id 40944, offset 0, flags [DF], proto ICMP (1), length 84)
    10.0.0.10 > 10.0.0.10: ICMP echo request, id 21, seq 5, length 64

Each echo request comes back ~0.2 ms later as the same packet (same IP ID, TTL unchanged), with only the destination rewritten. TCP shows the same pattern:

$ tcpdump -ni enp5s0 -e -v tcp port 22 and host 10.0.0.10
15:38:10.992586 b6:1c:3e:a5:ce:17 > fe:7d:a0:0c:fe:c5, ethertype IPv4 (0x0800), length 74: (tos 0x0, ttl 64, id 46865, offset 0, flags [DF], proto TCP (6), length 60)
    10.0.0.10.53888 > 194.11.242.75.22: Flags [S], cksum 0xbe8f (incorrect -> 0xd1f4), seq 3200581619, win 64240, options [mss 1460,sackOK,TS val 2633272949 ecr 0,nop,wscale 7], length 0
15:38:10.992666 fe:7d:a0:0c:fe:c5 > b6:1c:3e:a5:ce:17, ethertype IPv4 (0x0800), length 74: (tos 0x0, ttl 64, id 46865, offset 0, flags [DF], proto TCP (6), length 60)
    10.0.0.10.53888 > 10.0.0.10.22: Flags [S], cksum 0x7c42 (correct), seq 3200581619, win 64240, options [mss 1460,sackOK,TS val 2633272949 ecr 0,nop,wscale 7], length 0

(b6:1c:3e:a5:ce:17 = VM NIC, fe:7d:a0:0c:fe:c5 = gateway; the outgoing cksum incorrect is just checksum offload at the capture point.)

Control, captured on the same VM:

Client → 194.11.242.75 Result Arrives at VM as
Neighbor VM 10.0.0.11 (same network, other hypervisor, no VIP) ok 194.11.242.60 > 10.0.0.10
The VM itself fails 10.0.0.10 > 10.0.0.10

Expected behavior

The hairpin flow is also source-NATed (to the VIP, or to the NAT IP as in the neighbor case). The VM then receives e.g. 194.11.242.75 > 10.0.0.10, and conntrack translates the reply back.

Analysis (code at 6aa1532)

  1. dnat_node.c#L45-L82: VIP match, the destination is rewritten and DP_FLOW_FLAG_DST_NAT is set.
  2. ipv4_lookup_node.c#L50: the new destination is a local VF, so flow_type = DP_FLOW_WEST_EAST.
  3. snat_node.c#L168-L176: SNAT only runs for DP_FLOW_HAS_NO_FLAGS and DP_FLOW_SOUTH_NORTH. Both are false, so SNAT is skipped.

The neighbor on another hypervisor has no local DNAT entry. It takes the normal SNAT → south-north path and is DNATed on arrival, which is why that case works. Only in_port == DNAT target breaks, because then src == dst.

Contributor guide

No contributing guide indexed for this repository

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start with dnat_node.c lines 45-82, ipv4_lookup_node.c line 50, and snat_node.c lines 168-176 to trace the hairpin flow and its flags. Verify the change against the ICMP and TCP reproductions: traffic from a VM to its own VIP should be source-NATed before returning to the local VM, with replies translated by conntrack.

Written by the indexing model from the issue text.

Assessment

Tech stack
c
Domain
networking
Issue type
Bug
Difficulty
4/5
Estimated time
3-5 days
Activity status
Active
Clarity
Clearly specified
Newbie friendliness
58/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.