NAT Loopback (Hairpin) isn't working: No rewriting Occuring OpenWrt 23.05.2

I have configured my firewall to enable NAT loopback, I have tried a number of other posts' advice. I have tried manually adding iptable rules with DNAT + SNAT. I have done multiple network captures on eth1 and I am not seeing packets being rewritten on the first hop that should be marked as matching the rule. NOTE: please focus on service-443 because that is the one I am trying to get working first (NOT 80). The OpenWrt 23.05.2 build I am using is FriendlyWRT which I believe has both fw4 and iptables running so I am not sure if that is the cause.

Firewall

config defaults
option input 'ACCEPT'
option output 'ACCEPT'
option forward 'ACCEPT'
option flow_offloading '1'
option synflood_protect '1'
option drop_invalid '1'

config zone
option name 'lan'
list network 'lan'
option input 'ACCEPT'
option output 'ACCEPT'
option forward 'ACCEPT'

config zone
option name 'wan'
list network 'wan'
list network 'wan6'
option input 'REJECT'
option output 'ACCEPT'
option forward 'REJECT'
option fullcone4 '1'
option fullcone6 '1'
option masq '1'
option mtu_fix '1'

config forwarding
option src 'lan'
option dest 'wan'

config rule
option name 'Allow-DHCP-Renew'
option src 'wan'
option proto 'udp'
option dest_port '68'
option target 'ACCEPT'
option family 'ipv4'

config rule
option name 'Allow-Ping'
option src 'wan'
option proto 'icmp'
option icmp_type 'echo-request'
option family 'ipv4'
option target 'ACCEPT'
option enabled '0'

config rule
option name 'Allow-IGMP'
option src 'wan'
option proto 'igmp'
option family 'ipv4'
option target 'ACCEPT'

config rule
option name 'Allow-DHCPv6'
option src 'wan'
option proto 'udp'
option dest_port '546'
option family 'ipv6'
option target 'ACCEPT'

config rule
option name 'Allow-MLD'
option src 'wan'
option proto 'icmp'
option src_ip 'fe80::/10'
list icmp_type '130/0'
list icmp_type '131/0'
list icmp_type '132/0'
list icmp_type '143/0'
option family 'ipv6'
option target 'ACCEPT'

config rule
option name 'Allow-ICMPv6-Input'
option src 'wan'
option proto 'icmp'
list icmp_type 'echo-request'
list icmp_type 'echo-reply'
list icmp_type 'destination-unreachable'
list icmp_type 'packet-too-big'
list icmp_type 'time-exceeded'
list icmp_type 'bad-header'
list icmp_type 'unknown-header-type'
list icmp_type 'router-solicitation'
list icmp_type 'neighbour-solicitation'
list icmp_type 'router-advertisement'
list icmp_type 'neighbour-advertisement'
option limit '1000/sec'
option family 'ipv6'
option target 'ACCEPT'

config rule
option name 'Allow-ICMPv6-Forward'
option src 'wan'
option dest '*'
option proto 'icmp'
list icmp_type 'echo-request'
list icmp_type 'echo-reply'
list icmp_type 'destination-unreachable'
list icmp_type 'packet-too-big'
list icmp_type 'time-exceeded'
list icmp_type 'bad-header'
list icmp_type 'unknown-header-type'
option limit '1000/sec'
option family 'ipv6'
option target 'ACCEPT'

config rule
option name 'Allow-IPSec-ESP'
option src 'wan'
option dest 'lan'
option proto 'esp'
option target 'ACCEPT'

config rule
option name 'Allow-ISAKMP'
option src 'wan'
option dest 'lan'
option dest_port '500'
option proto 'udp'
option target 'ACCEPT'

config rule
option name 'Reject-IPv6'
option family 'ipv6'
option src 'wan'
option dest '*'
option target 'REJECT'
option enabled '0'

config zone 'docker'
option input 'ACCEPT'
option output 'ACCEPT'
option forward 'ACCEPT'
option name 'docker'
list network 'docker'

config redirect
option dest 'lan'
option target 'DNAT'
option name 'services-80'
option src 'wan'
option src_dport '80'
option dest_ip '192.168.1.4'
option dest_port '80'
option reflection '0'

config redirect
option dest 'lan'
option target 'DNAT'
option name 'service-443'
option src 'wan'
option src_dport '443'
option dest_ip '192.168.1.4'
option dest_port '443'
option src_ip '192.168.1.0/24'
option enabled '1'
option proto 'tcp udp'

network

config interface 'loopback'
option device 'lo'
option proto 'static'
option ipaddr '127.0.0.1'
option netmask '255.0.0.0'

config globals 'globals'
option ula_prefix 'xxxxxx'

config device
option name 'eth0'
option macaddr 'xxxxxxxx'

config interface 'wan'
option device 'eth0'
option proto 'dhcp'

config interface 'wan6'
option device 'eth0'
option proto 'dhcpv6'

config device
option name 'br-lan'
option type 'bridge'
list ports 'eth1'

config device
option name 'eth1'
option macaddr 'xxxxx'
option acceptlocal '1'

config interface 'lan'
option device 'br-lan'
option proto 'static'
list ipaddr '192.168.1.1/24'
option delegate '0'
list dns '192.168.1.4'

config interface 'docker'
option device 'docker0'
option proto 'none'
option auto '0'

config device
option type 'bridge'
option name 'docker0'

1 Like

Please provide the output of nft list ruleset

FYI - I received this warning when running the command

# Warning: table ip filter is managed by iptables-nft, do not touch!
# Warning: table ip nat is managed by iptables-nft, do not touch!

NFT List Ruleset

table inet fw4 {
	ct helper amanda {
		type "amanda" protocol udp
		l3proto inet
	}

	ct helper ftp {
		type "ftp" protocol tcp
		l3proto inet
	}

	ct helper RAS {
		type "RAS" protocol udp
		l3proto inet
	}

	ct helper Q.931 {
		type "Q.931" protocol tcp
		l3proto inet
	}

	ct helper irc {
		type "irc" protocol tcp
		l3proto ip
	}

	ct helper pptp {
		type "pptp" protocol tcp
		l3proto ip
	}

	ct helper sip {
		type "sip" protocol udp
		l3proto inet
	}

	ct helper snmp {
		type "snmp" protocol udp
		l3proto ip
	}

	ct helper tftp {
		type "tftp" protocol udp
		l3proto inet
	}

	flowtable ft {
		hook ingress priority filter
		devices = { docker0, eth0, eth1 }
		counter
	}

	chain input {
		type filter hook input priority filter; policy accept;
		iifname "lo" accept comment "!fw4: Accept traffic from loopback"
		ct state established,related accept comment "!fw4: Allow inbound established and related flows"
		ct state invalid drop comment "!fw4: Drop flows with invalid conntrack state"
		tcp flags syn / fin,syn,rst,ack jump syn_flood comment "!fw4: Rate limit TCP syn packets"
		iifname "br-lan" jump input_lan comment "!fw4: Handle lan IPv4/IPv6 input traffic"
		iifname "eth0" jump input_wan comment "!fw4: Handle wan IPv4/IPv6 input traffic"
		iifname "docker0" jump input_docker comment "!fw4: Handle docker IPv4/IPv6 input traffic"
	}

	chain forward {
		type filter hook forward priority filter; policy accept;
		meta l4proto { tcp, udp } flow add @ft
		ct state established,related accept comment "!fw4: Allow forwarded established and related flows"
		ct state invalid drop comment "!fw4: Drop flows with invalid conntrack state"
		iifname "br-lan" jump forward_lan comment "!fw4: Handle lan IPv4/IPv6 forward traffic"
		iifname "eth0" jump forward_wan comment "!fw4: Handle wan IPv4/IPv6 forward traffic"
		iifname "docker0" jump forward_docker comment "!fw4: Handle docker IPv4/IPv6 forward traffic"
		jump upnp_forward comment "Hook into miniupnpd forwarding chain"
	}

	chain output {
		type filter hook output priority filter; policy accept;
		oifname "lo" accept comment "!fw4: Accept traffic towards loopback"
		ct state established,related accept comment "!fw4: Allow outbound established and related flows"
		ct state invalid drop comment "!fw4: Drop flows with invalid conntrack state"
		oifname "br-lan" jump output_lan comment "!fw4: Handle lan IPv4/IPv6 output traffic"
		oifname "eth0" jump output_wan comment "!fw4: Handle wan IPv4/IPv6 output traffic"
		oifname "docker0" jump output_docker comment "!fw4: Handle docker IPv4/IPv6 output traffic"
	}

	chain prerouting {
		type filter hook prerouting priority filter; policy accept;
		iifname "br-lan" jump helper_lan comment "!fw4: Handle lan IPv4/IPv6 helper assignment"
		iifname "docker0" jump helper_docker comment "!fw4: Handle docker IPv4/IPv6 helper assignment"
	}

	chain handle_reject {
		meta l4proto tcp reject with tcp reset comment "!fw4: Reject TCP traffic"
		reject comment "!fw4: Reject any other traffic"
	}

	chain syn_flood {
		limit rate 25/second burst 50 packets return comment "!fw4: Accept SYN packets below rate-limit"
		drop comment "!fw4: Drop excess packets"
	}

	chain input_lan {
		ct status dnat accept comment "!fw4: Accept port redirections"
		jump accept_from_lan
	}

	chain output_lan {
		jump accept_to_lan
	}

	chain forward_lan {
		jump accept_to_wan comment "!fw4: Accept lan to wan forwarding"
		ct status dnat accept comment "!fw4: Accept port forwards"
		jump accept_to_lan
	}

	chain helper_lan {
		udp dport 10080 ct helper set "amanda" comment "!fw4: Amanda backup and archiving proto"
		tcp dport 21 ct helper set "ftp" comment "!fw4: FTP passive connection tracking"
		udp dport 1719 ct helper set "RAS" comment "!fw4: RAS proto tracking"
		tcp dport 1720 ct helper set "Q.931" comment "!fw4: Q.931 proto tracking"
		meta nfproto ipv4 tcp dport 6667 ct helper set "irc" comment "!fw4: IRC DCC connection tracking"
		meta nfproto ipv4 tcp dport 1723 ct helper set "pptp" comment "!fw4: PPTP VPN connection tracking"
		udp dport 5060 ct helper set "sip" comment "!fw4: SIP VoIP connection tracking"
		meta nfproto ipv4 udp dport 161 ct helper set "snmp" comment "!fw4: SNMP monitoring connection tracking"
		udp dport 69 ct helper set "tftp" comment "!fw4: TFTP connection tracking"
	}

	chain accept_from_lan {
		iifname "br-lan" counter packets 26922 bytes 5711023 accept comment "!fw4: accept lan IPv4/IPv6 traffic"
	}

	chain accept_to_lan {
		oifname "br-lan" counter packets 412 bytes 65940 accept comment "!fw4: accept lan IPv4/IPv6 traffic"
	}

	chain input_wan {
		meta nfproto ipv4 udp dport 68 counter packets 0 bytes 0 accept comment "!fw4: Allow-DHCP-Renew"
		meta nfproto ipv4 meta l4proto igmp counter packets 0 bytes 0 accept comment "!fw4: Allow-IGMP"
		meta nfproto ipv6 udp dport 546 counter packets 3 bytes 423 accept comment "!fw4: Allow-DHCPv6"
		ip6 saddr fe80::/10 icmpv6 type . icmpv6 code { mld-listener-query . no-route, mld-listener-report . no-route, mld-listener-done . no-route, mld2-listener-report . no-route } counter packets 0 bytes 0 accept comment "!fw4: Allow-MLD"
		icmpv6 type { destination-unreachable, time-exceeded, echo-request, echo-reply, nd-router-solicit, nd-router-advert } limit rate 1000/second counter packets 17 bytes 1088 accept comment "!fw4: Allow-ICMPv6-Input"
		icmpv6 type . icmpv6 code { packet-too-big . no-route, parameter-problem . no-route, nd-neighbor-solicit . no-route, nd-neighbor-advert . no-route, parameter-problem . admin-prohibited } limit rate 1000/second counter packets 0 bytes 0 accept comment "!fw4: Allow-ICMPv6-Input"
		ct status dnat accept comment "!fw4: Accept port redirections"
		jump reject_from_wan
	}

	chain output_wan {
		jump accept_to_wan
	}

	chain forward_wan {
		icmpv6 type { destination-unreachable, time-exceeded, echo-request, echo-reply } limit rate 1000/second counter packets 0 bytes 0 accept comment "!fw4: Allow-ICMPv6-Forward"
		icmpv6 type . icmpv6 code { packet-too-big . no-route, parameter-problem . no-route, parameter-problem . admin-prohibited } limit rate 1000/second counter packets 0 bytes 0 accept comment "!fw4: Allow-ICMPv6-Forward"
		meta l4proto esp counter packets 0 bytes 0 jump accept_to_lan comment "!fw4: Allow-IPSec-ESP"
		udp dport 500 counter packets 0 bytes 0 jump accept_to_lan comment "!fw4: Allow-ISAKMP"
		ct status dnat accept comment "!fw4: Accept port forwards"
		jump reject_to_wan
	}

	chain accept_to_wan {
		meta nfproto ipv4 oifname "eth0" ct state invalid counter packets 0 bytes 0 drop comment "!fw4: Prevent NAT leakage"
		oifname "eth0" counter packets 9116 bytes 3396584 accept comment "!fw4: accept wan IPv4/IPv6 traffic"
	}

	chain reject_from_wan {
		iifname "eth0" counter packets 2083 bytes 87327 jump handle_reject comment "!fw4: reject wan IPv4/IPv6 traffic"
	}

	chain reject_to_wan {
		oifname "eth0" counter packets 0 bytes 0 jump handle_reject comment "!fw4: reject wan IPv4/IPv6 traffic"
	}

	chain input_docker {
		jump accept_from_docker
	}

	chain output_docker {
		jump accept_to_docker
	}

	chain forward_docker {
		jump accept_to_docker
	}

	chain helper_docker {
		udp dport 10080 ct helper set "amanda" comment "!fw4: Amanda backup and archiving proto"
		tcp dport 21 ct helper set "ftp" comment "!fw4: FTP passive connection tracking"
		udp dport 1719 ct helper set "RAS" comment "!fw4: RAS proto tracking"
		tcp dport 1720 ct helper set "Q.931" comment "!fw4: Q.931 proto tracking"
		meta nfproto ipv4 tcp dport 6667 ct helper set "irc" comment "!fw4: IRC DCC connection tracking"
		meta nfproto ipv4 tcp dport 1723 ct helper set "pptp" comment "!fw4: PPTP VPN connection tracking"
		udp dport 5060 ct helper set "sip" comment "!fw4: SIP VoIP connection tracking"
		meta nfproto ipv4 udp dport 161 ct helper set "snmp" comment "!fw4: SNMP monitoring connection tracking"
		udp dport 69 ct helper set "tftp" comment "!fw4: TFTP connection tracking"
	}

	chain accept_from_docker {
		iifname "docker0" counter packets 0 bytes 0 accept comment "!fw4: accept docker IPv4/IPv6 traffic"
	}

	chain accept_to_docker {
		oifname "docker0" counter packets 0 bytes 0 accept comment "!fw4: accept docker IPv4/IPv6 traffic"
	}

	chain dstnat {
		type nat hook prerouting priority dstnat; policy accept;
		iifname "br-lan" jump dstnat_lan comment "!fw4: Handle lan IPv4/IPv6 dstnat traffic"
		iifname "eth0" jump dstnat_wan comment "!fw4: Handle wan IPv4/IPv6 dstnat traffic"
		jump upnp_prerouting comment "Hook into miniupnpd prerouting chain"
	}

	chain srcnat {
		type nat hook postrouting priority srcnat; policy accept;
		oifname "br-lan" jump srcnat_lan comment "!fw4: Handle lan IPv4/IPv6 srcnat traffic"
		oifname "eth0" jump srcnat_wan comment "!fw4: Handle wan IPv4/IPv6 srcnat traffic"
		jump upnp_postrouting comment "Hook into miniupnpd postrouting chain"
	}

	chain dstnat_lan {
		ip saddr 192.168.1.0/24 ip daddr <wan ip> tcp dport 443 dnat ip to <serverip>:443 comment "!fw4: service-443 (reflection)"
		ip saddr 192.168.1.0/24 ip daddr <wan ip> udp dport 443 dnat ip to <serverip>:443 comment "!fw4: service-443 (reflection)"
	}

	chain srcnat_lan {
		ip saddr 192.168.1.0/24 ip daddr <serverip> tcp dport 443 snat ip to 192.168.1.1 comment "!fw4: service-443 (reflection)"
		ip saddr 192.168.1.0/24 ip daddr <serverip> udp dport 443 snat ip to 192.168.1.1 comment "!fw4: service-443 (reflection)"
	}

	chain dstnat_wan {
		meta nfproto ipv4 tcp dport 80 counter packets 9 bytes 452 dnat ip to <serverip>:80 comment "!fw4: services-80"
		meta nfproto ipv4 udp dport 80 counter packets 0 bytes 0 dnat ip to <serverip>:80 comment "!fw4: services-80"
		meta nfproto ipv4 tcp dport 443 counter packets 102 bytes 7781 dnat ip to <serverip>:443 comment "!fw4: service-443"
		meta nfproto ipv4 udp dport 443 counter packets 0 bytes 0 dnat ip to <serverip>:443 comment "!fw4: service-443"
	}

	chain srcnat_wan {
		meta nfproto ipv4 masquerade comment "!fw4: Masquerade IPv4 wan traffic"
	}

	chain raw_prerouting {
		type filter hook prerouting priority raw; policy accept;
	}

	chain raw_output {
		type filter hook output priority raw; policy accept;
	}

	chain mangle_prerouting {
		type filter hook prerouting priority mangle; policy accept;
	}

	chain mangle_postrouting {
		type filter hook postrouting priority mangle; policy accept;
	}

	chain mangle_input {
		type filter hook input priority mangle; policy accept;
	}

	chain mangle_output {
		type route hook output priority mangle; policy accept;
	}

	chain mangle_forward {
		type filter hook forward priority mangle; policy accept;
		iifname "eth0" tcp flags syn tcp option maxseg size set rt mtu comment "!fw4: Zone wan IPv4/IPv6 ingress MTU fixing"
		oifname "eth0" tcp flags syn tcp option maxseg size set rt mtu comment "!fw4: Zone wan IPv4/IPv6 egress MTU fixing"
	}

	chain upnp_forward {
	}

	chain upnp_prerouting {
	}

	chain upnp_postrouting {
	}
}
table ip filter {
	chain DOCKER-USER {
		iifname "eth0" oifname "docker0" counter packets 0 bytes 0 xt target "REJECT"
		counter packets 2890222 bytes 2269747026 return
	}

	chain DOCKER {
	}

	chain DOCKER-ISOLATION-STAGE-1 {
		iifname "docker0" oifname != "docker0" counter packets 0 bytes 0 jump DOCKER-ISOLATION-STAGE-2
		counter packets 2890222 bytes 2269747026 return
	}

	chain DOCKER-ISOLATION-STAGE-2 {
		oifname "docker0" counter packets 0 bytes 0 drop
		counter packets 0 bytes 0 return
	}

	chain FORWARD {
		type filter hook forward priority filter; policy accept;
		counter packets 2890222 bytes 2269747026 jump DOCKER-USER
		counter packets 2890222 bytes 2269747026 jump DOCKER-ISOLATION-STAGE-1
		oifname "docker0" xt match "conntrack" counter packets 0 bytes 0 accept
		oifname "docker0" counter packets 0 bytes 0 jump DOCKER
		iifname "docker0" oifname != "docker0" counter packets 0 bytes 0 accept
		iifname "docker0" oifname "docker0" counter packets 0 bytes 0 accept
	}
}
table inet nft-qos-monitor {
	chain upload {
		type filter hook postrouting priority filter; policy accept;
		ip saddr 192.168.1.189 counter packets 3783 bytes 882459
		ip saddr 192.168.1.130 counter packets 11628 bytes 1002514
		ip saddr 192.168.1.133 counter packets 4801 bytes 260334
		ip saddr 192.168.1.166 counter packets 5173 bytes 901522
		ip saddr 192.168.1.145 counter packets 11684 bytes 1030998
		ip saddr 192.168.1.126 counter packets 0 bytes 0
		ip saddr 192.168.1.185 counter packets 2512089 bytes 2190263305
		ip saddr 192.168.1.104 counter packets 67075 bytes 14199376
		ip saddr 192.168.1.105 counter packets 10884 bytes 932359
		ip saddr 192.168.1.165 counter packets 12339 bytes 1176987
		ip saddr 192.168.1.192 counter packets 378 bytes 15932
		ip saddr 192.168.1.127 counter packets 1030 bytes 104153
		ip saddr 192.168.1.131 counter packets 843 bytes 88975
		ip saddr 192.168.1.186 counter packets 2423 bytes 162531
		ip saddr 192.168.1.164 counter packets 608 bytes 41277
		ip saddr 192.168.1.193 counter packets 16 bytes 864
		ip saddr 192.168.1.195 counter packets 33567 bytes 6133947
		ip saddr 192.168.1.147 counter packets 332 bytes 16930
		ip saddr 192.168.1.178 counter packets 3699 bytes 219641
		ip saddr 192.168.1.136 counter packets 58 bytes 3968
		ip saddr 192.168.1.102 counter packets 10040 bytes 946222
		ip saddr 192.168.1.111 counter packets 1453 bytes 154283
		ip saddr 192.168.1.183 counter packets 34531 bytes 7385936
		ip saddr 192.168.1.135 counter packets 2742 bytes 801817
		ip saddr 192.168.1.173 counter packets 459 bytes 45435
		ip saddr 192.168.1.179 counter packets 1136 bytes 155179
		ip saddr 192.168.1.172 counter packets 227 bytes 20628
		ip saddr 192.168.1.112 counter packets 86 bytes 17763
	}

	chain download {
		type filter hook prerouting priority filter; policy accept;
		ip daddr 192.168.1.189 counter packets 942 bytes 248455
		ip daddr 192.168.1.130 counter packets 4834 bytes 695104
		ip daddr 192.168.1.133 counter packets 2557 bytes 149547
		ip daddr 192.168.1.166 counter packets 3414 bytes 725783
		ip daddr 192.168.1.145 counter packets 5110 bytes 747656
		ip daddr 192.168.1.126 counter packets 10 bytes 710
		ip daddr 192.168.1.185 counter packets 53 bytes 3336
		ip daddr 192.168.1.104 counter packets 31547 bytes 1913426
		ip daddr 192.168.1.105 counter packets 3681 bytes 482584
		ip daddr 192.168.1.165 counter packets 12438 bytes 1381919
		ip daddr 192.168.1.192 counter packets 190 bytes 8108
		ip daddr 192.168.1.127 counter packets 1279 bytes 72310
		ip daddr 192.168.1.131 counter packets 1158 bytes 64578
		ip daddr 192.168.1.186 counter packets 959 bytes 51641
		ip daddr 192.168.1.164 counter packets 163 bytes 9763
		ip daddr 192.168.1.193 counter packets 8 bytes 448
		ip daddr 192.168.1.195 counter packets 12164 bytes 17168664
		ip daddr 192.168.1.147 counter packets 354 bytes 20308
		ip daddr 192.168.1.178 counter packets 2581 bytes 142348
		ip daddr 192.168.1.136 counter packets 119 bytes 9340
		ip daddr 192.168.1.102 counter packets 3399 bytes 541809
		ip daddr 192.168.1.111 counter packets 1124 bytes 111268
		ip daddr 192.168.1.183 counter packets 2381 bytes 188257
		ip daddr 192.168.1.135 counter packets 2103 bytes 596623
		ip daddr 192.168.1.173 counter packets 772 bytes 89390
		ip daddr 192.168.1.179 counter packets 1506 bytes 179441
		ip daddr 192.168.1.172 counter packets 137 bytes 17555
		ip daddr 192.168.1.112 counter packets 70 bytes 15370
	}
}
table ip nat {
	chain DOCKER {
		iifname "docker0" counter packets 0 bytes 0 return
	}

	chain POSTROUTING {
		type nat hook postrouting priority srcnat; policy accept;
		oifname != "docker0" ip saddr 172.17.0.0/16 counter packets 0 bytes 0 xt target "MASQUERADE"
	}

	chain PREROUTING {
		type nat hook prerouting priority dstnat; policy accept;
		xt match "addrtype" counter packets 22976 bytes 1295604 jump DOCKER
	}

	chain OUTPUT {
		type nat hook output priority -100; policy accept;
		ip daddr != 127.0.0.0/8 xt match "addrtype" counter packets 0 bytes 0 jump DOCKER
	}
}
  • Are you referring to this rule?
  • If so don't see any of the hairpin configs (e.g. reflection zone, IP to use, etc.)

Yes, that is the one. The wan uses the external ip. The dest_ip is the server, option reflection 1 is default.

The firewall gui interface has the NAT loopback. The reflection zone is dest 'lan'.

The traffic to hairpin is coming from lan and needs to go back through lan.

Is the hairpin config an additional different config?

I tried to add a reflection rule separate from wan to lan rule. It still didn't work. I am not seeing any rewritten syn packet with the destination 192.168.1.4

config redirect
    option dest 'lan'
    option target 'DNAT'
    option src 'lan'
    option src_dip '<WAN IP>'
    option src_dport '443'
    option dest_ip '192.168.1.4'
    option dest_port '443'
    list reflection_zone 'lan'

Can you share with me the config you reference that places the packet in the source network? 22.03.2 - NAT reflection / hairpinning? - #2 by lleachii

My updated partial firewall config

config redirect
    option dest 'lan'
    option target 'DNAT'
    option name 'services-80'
    option src 'wan'
    option src_dport '80'
    option dest_ip '192.168.1.4'
    option dest_port '80'
    list reflection_zone 'lan'

config redirect
    option dest 'lan'
    option target 'DNAT'
    option name 'service-443'
    option src 'wan'
    option src_dport '443'
    option dest_ip '192.168.1.4'
    option dest_port '443'
    list reflection_zone 'lan'

config rule
    option src 'lan'
    option dest 'lan'
    option target 'ACCEPT'
    list dest_ip '<wan external ip>'

config zone
    option name 'lantolan'
    option input 'ACCEPT'
    option output 'ACCEPT'
    option forward 'ACCEPT'
    option log '1'
    option log_limit '10'
    list network 'lan'

config forwarding
    option src 'lantolan'
    option dest 'lan'

config forwarding
    option src 'lan'
    option dest 'lantolan'

Well, studying @lleachii 's replies for other posts with the same problem I seem to have solution and a theory.

The Problem: apparently, modern Linux kernels will not route traffic destined for the same subnet on the same interface. This is probably to avoid duplication of packets in the case that a local subnet packet is sent to the gateway, the gateway doesn't want to forward the packet because it is assumed it reached the actual IP on that LAN subnet and interface.

For network devices with multiple interfaces, this isn't really a problem because there is a switch layer that will move packets between interfaces. On the NanoPi R2S, there is only one LAN interface and no switch as a result. Therefore, some of the solutions posted involved creating a switch interface and then creating a VLAN to tag redirected packets to get them outside the kernel routing pipeline that doesn't forward the packet.

The solution: well, I was trying to further debug this issue when I started to tcpdump different "interfaces" on the host. Upon doing so, I found that reflection mysteriously started working as expected when dumping a specific interface (br-lan). Therefore, to get NAT reflection (loopback) working I ran:

tcpdump -i br-lan dst host <wanip>

Interestingly, there is no packets that hit that filter and therefore, I see no degradation of bandwidth from allowing it to run.

I created this init script to start the dump on boot

#!/bin/sh /etc/rc.common

START=30
STOP=20

WANT_IF=wan
_pid=-1
start() {
        if [ -f /var/run/nat-reflection.pid ];then
             _pid=$(cat /var/run/nat-reflection.pid | head -1)
             echo "Nat-reflection already started ${_pid}"
        else
         source /lib/functions/network.sh
         # commands to launch application
          network_get_ipaddr ipaddr "$WANT_IF"
         tcpdump -i br-lan dst host $ipaddr &
         _pid=$!
         echo "$_pid" > /var/run/nat-reflection.pid
         echo "Nat-reflection started(${_pid})"
        fi
}

stop() {
        # commands to kill application
        if [ -f /var/run/nat-reflection.pid ];then
            _pid=$(cat /var/run/nat-reflection.pid | head -1)
            kill -9 ${_pid}
            rm /var/run/nat-reflection.pid
            echo "Nat-reflection(${pid}) stopped)"
        else
            echo "No nat-reflection pid found"
        fi
}

I have not tested this extensively but just enough to see the tcpdump started and is running. You will need to install tcpdump via opkg. Finally, if anyone has other ideas on how to make this better please let me know. But I have already spent many many hours on this dumb problem so I'll stick with what works and ignore the deep technical details why it works....

3 Likes

Starting tcpdump sets promiscuous mode as a side effect, that’s probably what you’re looking for.

In /etc/config/network:

config device 
  option name br-lan
  option promisc 1

Alternatively:
ip link set br-lan promisc on

1 Like

This topic was automatically closed 10 days after the last reply. New replies are no longer allowed.