EHfive / einat-ebpf

An eBPF-based Endpoint-Independent(Full Cone) NAT for Linux
GNU General Public License v2.0
151 stars 7 forks source link

Hairpinning routing issue #4

Closed EHfive closed 5 months ago

EHfive commented 8 months ago

https://datatracker.ietf.org/doc/html/rfc4787#section-6

By default, our created external mapping port is only accessible by BPF program.

Hairpinning requires to access external mapping port from internal, so the internal packets must be routed to BPF program in order to be SNAT and rev-SNAT to another internal host.

However, this is not possible with default route rule as Linux would create local routing entry that forward packet towards external mapping port to local host instead of network interface and BPF program on TC egress hook.

So additional routing rule would be need to route those packet to our egress BPF program and route back processed packet from ingress to respective internal host.

EHfive commented 6 months ago

Using policy-based routing to route packets from internal interface intern0 and lookback lo to NAT external interface extern0 so hairpin packets can be processed.

External Interface External Address External Gateway External Interface MAC Address
extern0 192.168.1.100 192.168.1.1 f9:85:18:2a:80:d7
# add local rule with lower priority
ip rule add from all lookup local pref 200
# delete default local rule
ip rule del from all lookup local pref 0
# add hairpin rules with higher priority than local rule
ip rule add iif intern0 lookup 100 pref 100
ip rule add iif lo lookup 100 pref 101

ip route add 192.168.1.100 via 192.168.1.1 dev extern0 table 100

This requires resolving MAC address of gateway IP 192.168.1.1 through ARP, so it would not work if respective ARP record is not available. However we can add ARP record manually for the external address itself so the ARP record would always be available and correct. So for the last route entry, we instead do

ip neigh add 192.168.1.100 dev extern0 lladdr f9:85:18:2a:80:d7 nud permanent
ip route add 192.168.1.100 dev extern0 table 100

Then the traffic towards external address would be filled with destination mac address of external interface itself.