Yes
Routing requires two things:
- That kernel forwards packets to begin with
- That the firewall rules permit forwarding
Forward
For IPv4 – I don’t actually know what IPv6 uses – the net.ipv4.ip_forward
has to be ‘1’ for the first.
You can check with: sysctl net.ipv4.ip_forward
In my experience, merely the use of zone ‘external’ does set that. If it doesn’t, you can drop the config in:
# cat /etc/sysctl.d/i_am_router.conf
net.ipv4.ip_forward = 1
Filter
For the second, we need firewall rules. Again, I would first check what the current, actual rules are:
nft list ruleset
Alas, that is a mouthful since FirewallD is somewhat verbose when it writes rules. (For its own benefit, naturally.)
Until rather recently the FirewallD in RHEL (and hence Rocky) did lack proper support for router rules. Now there are policy objects. Chapter 40. Using and configuring firewalld Red Hat Enterprise Linux 8 | Red Hat Customer Portal
Therefore,
firewall-cmd --permanent --new-policy policy_int_to_ext
firewall-cmd --permanent --policy policy_int_to_ext --add-ingress-zone internal
firewall-cmd --permanent --policy policy_int_to_ext --add-egress-zone external
firewall-cmd --permanent --policy policy_int_to_ext --set-target ACCEPT
firewall-cmd --complete-reload
Which should make the chain filter_FORWARD look effectively (after dereferencing all jumps and gotos to auxiliary chains) something like:
ct state established,related accept
ct status dnat accept
iifname "lo" accept
ip6 daddr { ::/96, ::ffff:0.0.0.0/96, 2002::/24, 2002:a00::/24, 2002:7f00::/24, 2002:a9fe::/32, 2002:ac10::/28, 2002:c0a8::/32, 2002:e000::/19 } reject with icmpv6 addr-unreachable
iifname "enp2s0" oifname "enp1s0" accept
meta l4proto { icmp, ipv6-icmp } counter packets 0 bytes 0 accept
ct state invalid drop
reject with icmpx admin-prohibited
There is (Ansible) RHEL System Role for firewall, but what features it supports and what not, you better check yourself: Chapter 40. Using and configuring firewalld Red Hat Enterprise Linux 8 | Red Hat Customer Portal
NAT
The masquerade, which is a form of sNAT is enabled in the ‘external’ zone default.
Your ruleset should already have oifname "enp1s0" masquerade
in within postrouting chain(s).
Quick check:
nft list ruleset | grep -10 masquerade
A router does not need masquerade, but it can make config in other machines simpler.
Lets have: WAN 192.168.2.0/24, LAN 192.168.3.0/24
The router has addresses 192.168.2.1 and 192.168.3.1.
The WAN has “upstream” router at 192.168.2.254, which is also the “gateway” for our router.
The router knows how to send to LAN, to WAN, and everything else goes to 192.168.2.254.
A client in LAN, 192.168.3.42, has gateway (i.e. default route) 192.168.3.1. It knows how to send to LAN and forwards everything else to 192.168.3.1.
A client in WAN, 192.168.2.7, has gateway 192.168.2.254. It knows how to send to WAN and forwards everything else to 192.168.2.254.
How does 192.168.2.7 send to 192.168.3.42? To 192.168.3.0/24?
Case A: router does not have sNAT
If packets are sent to 192.168.2.254, the same question repeats: How does 192.168.2.x send to 192.168.3.0/24?
These WAN clients (other than our router) need a “static route”: to 192.168.3.0/24 via 192.168.2.1
With that, the 192.168.2.7 would send packet to 192.168.2.1, rather than to 192.168.2.254, and 192.168.2.1 knows how to send to its LAN “neighbours”.
The positive side is that they could start a new connection from WAN to LAN. Well, they could if the router’s firewall would allow that. The policy above allows LAN to talk to WAN, but only replies from WAN to LAN.
Case B: router does have sNAT
The 192.168.3.42 did send a packet to 192.168.2.7.
The packet had SRC=192.168.3.42 DST=192.168.2.7
The masquerade at the router modifies the packet to read: SRC=192.168.2.1 DST=192.168.2.7
The 192.168.2.7 sends a reply that has SRC=192.168.2.7 DST=192.168.2.1
The router notes that this is a reply and modifies the packet to read: SRC=192.168.2.7 DST=192.168.3.42
(in prerouting)
Since the reply is not for the router, it is tossed out from the LAN port for 192.168.3.42 to pick up.
Nobody outside of LAN knows that the LAN exists.