RockyLinux dedicated router setup?

I have searched on how to create a router using RL however most of what I find appears to be outdated and/or incomplete.

Have have a Comcast Fiber Internet service that they’ve delivered. Usually any ISP that we’ve worked with hands you your subnet and reserves an address within your subnet as their gateway interface for you. In this case they’ve given the entire subnet and given us and additional /30 WAN subnet for our gateway/router. Since this isn’t something we normally see with Internet service we weren’t prepared with a dedicated router.

As a quick fix for this I would like to create a dedicated Linux router, preferable a Rocky Linux router however I find a lot of old info on how to do this. Some say to enable ipv4 forwarding in sysctl.conf which doesn’t appear to exist in RL9. Other say to use FirewallD/IPTables to forward traffic.

Has anyone done this? We need two way traffic forwarded, both outgoing and incoming. Is this as simple as creating a couple of forwarding rules in IPTables?

It does exist. You just have to set it in a sysctl.conf.

[root@router bin]# grep forward /etc/sysctl.d/99-sysctl.conf
net.ipv4.ip_forward = 1
net.ipv6.conf.all.forwarding = 1

Some use firewalld. You would set your WAN interface on the external zone, set your LAN interface on the internal zone. After, you would create a policy that allows the traffic to move.

nmcli con mod eth0 connection.zone external
nmcli con mod eth1 connection.zone internal
nmcli con up eth0
nmcli con up eth1
firewall-cmd --reload

# check that the interfaces have moved
firewall-cmd --list-all --zone=internal
firewall-cmd --list-all --zone=external

firewall-cmd --permanent --new-policy=internal-external
firewall-cmd --permanent --policy=internal-external --set-target=ACCEPT
firewall-cmd --permanent --policy=internal-external --add-masquerade
firewall-cmd --permanent --policy=internal-external --add-ingress-zone=internal
firewall-cmd --permanent --policy=internal-external --add-egress-zone=external
firewall-cmd --reload

I’ve exclusively used CentOS and now Rocky Linux as my home routers since 2008, so yes, it can be done. iptables isn’t the default used in Rocky Linux 9, you will need to use nftables if you’re not going to use firewalld. The nftables wiki has simple examples here if you don’t want to use firewalld.

3 Likes

Thanks! I am going to test this out but it seems cut and dry.
I do prefer to use FirewallD since it appears to be the de facto standard.

I did mine similar to @nazunalika with firewalld. Mostly as a proof of concept to myself as a VM, of which I then isolated a VM to use the Rocky firewalld instance for internet access and it worked a treat. I may at some point if I ever decide to relegate my Fortigate and replace it with something else, use this kind of solutions as it’s pretty neat. And you can add cockpit if you want to manage it with a web interface.

The IP forwarding determines what the kernel does when it receives a packet from outside that is not destined to this host.
The default, no forwarding, is to drop those packets.
If forwarding is enabled, then packet passes through and out from some interface.
In other words, the forwarding means whether the machine acts as router between subnets or not.

There are both IPv4 and IPv6. One could set each separately, i.e. enabling only for IPv4 is routing only IPv4 traffic.


IIRC, the use of ‘external’ FirewallD zone in any interface does enable forwarding too. If you use the FirewallD (like shown by @nazunalika ), then set the zones first and check whether that enables forwarding. If it does, then you don’t need to set them explicitly in sysctl.

I would not edit the /etc/sysctl.d/99-sysctl.conf anyway. I would add my customizations as new file, like /etc/sysctl.d/00-my-router.conf
That makes it easier for me to see later what I have done.


You can see all the rules that are currently in the kernel with:

nft list ruleset

On servers that have rather static setup I prever the nftables.service to the firewalld.service. IMHO, less is more in there.

I have a VM set up per @nazunalika’s instructions however I have a question. Looks like this will only forward inbound traffic from the inside but won’t forward outside traffic. My guess is that I would add the inverse versions of the ingress/egress lines? For the record, because of the network hardware setup I can’t test this until Monday at the earliest.

Would I add the same commands on the internal zone to get inbound Internet originating traffic to forward to a firewall on the “internal” zone?

This for example?

firewall-cmd --permanent --policy=internal-external --set-target=ACCEPT
firewall-cmd --permanent --policy=internal-external --add-masquerade
firewall-cmd --permanent --policy=internal-external --add-ingress-zone=external
firewall-cmd --permanent --policy=internal-external --add-egress-zone=internal

Definitely not.

What the FirewallD with the above internal-external policy does is a simple edge router.
The assumption is that the internal (LAN) is a private subnet and that is why there is masquerade (i.e. sNAT) on “outgoing” traffic.

The main chain for routed traffic is

chain filter_FORWARD { # handle 166
	type filter hook forward priority filter + 10; policy accept;
	ct state { established, related } accept # handle 179
	ct status dnat accept # handle 180
	iifname "lo" accept # handle 181
	ct state invalid drop # handle 182
	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 # handle 201
	jump filter_FORWARD_ZONES # handle 185
	reject with icmpx admin-prohibited # handle 187
}

The “handle 185” above leads to the policies that allow (some) new traffic to go through.
The “handle 179” above allows those connections to continue.
The ‘internal-external’ policy allows all new connections, everything from LAN to WAN.


However, there is the masquerade. For anyone in the external zone (WAN), there are no “hosts in LAN”. They know and see only one host, the public IP address of the router. That is the only host that they can start to talk to.

If you want someone on the WAN to be able to start a connection to someone in the LAN, then the router must have a port forward rule (a DNAT) that reroutes connection attempt to specific port of router into specific host in LAN.


The alternative would be to remove the masquerade, but that is possible only if all the hosts in the LAN have public addresses.

Thanks @jlehtone. Just a quick response here, I will have more time to work on this later today.
For some reason that masq line escaped me. All addresses that we have access to are public addresses. We need this to be a pure router, not a forwarding and masquerading system. We have a firewall behind this device to do this. So while I’ve labelled the /30 as the WAN in this case the /29 is also publicly accessible addresses that we need to allow access to for DNS, WWW, VPN.
Generally I’ve seen ISPs just hand you your /29 and deal with the PTP themselves. Not sure why they’re doing it this way this time.

In that case I’d recommend the nftables.service approach as the required ruleset is very short and simple (for the forward filter).

@jlehtone , For a clean slate I’ve wiped this VM clean and started with just adding IP forwarding via sysctl.conf. Surprisingly that’s all I needed to do to get it to forward traffic in and out. As a test I set up a new firewall behind this router, pointed my personal PC at it for outbound web traffic, and set up a VPN into the test firewall. So far all seem to work fine. FirewallD is still the default front end and the output of “firewall-cmd” shows “forward: yes” just for adding the sysctl.conf entries. I’ve diverted the SSH port as well as restricted what IPs can access SSH but beyond that there’s so little to actually do to get this working. This seems way too easy and makes me wonder if this isn’t ideal. I am currently banging away at it with nmap to see what it responds to and so far as expected it responds to nothing, save for that one port via that one IP.
What am I missing? Why does this seem wrong (mostly because it was too easy).

Edit: for example: nmap -U shows:
135/udp open|filtered msrpc
137/udp open|filtered netbios-ns
138/udp open|filtered netbios-dgm
139/udp open|filtered netbios-ssn
162/udp open|filtered snmptrap
445/udp open|filtered microsoft-ds

However another machine running the same nmap does not show the same UDP ports at all. It shows:
53/udp filtered domain
5060/udp filtered sip

None of these services are even installed to be running. Sure they’re “filtered” but it’s interesting they show up at all.

As I said earlier:
nft list ruleset
if/when you learn its syntax, you should be able to tell what the rules do to different packets.

To expand on the use of nftables.service:
Do look at /etc/sysconfig/nftables.conf
It essentially says:

if you like the current ruleset (made by FirewallD or whatever), then
nft list ruleset >/etc/sysconfig/wolf.conf
and append to /etc/sysconfig/nftables.conf:
include "/etc/nftables/wolf.nft"
If you don’t like the ruleset, then modify the /etc/nftables/wolf.nft to your liking.

What it does not say is how to switch services:

systemctl mask --now firewalld.service
systemctl enable --now nftables.service

Note also that there is a very minimal ruleset in /etc/nftables/main.nft
and it could include minimal rules for routing, the /etc/nftables/router.nft:

# Sample configuration snippet for nftables service.
# Meant to be included by main.nft, not for direct use.

# a common table for both IPv4 and IPv6
table inet nftables_svc {

	# base-chain for traffic forwarded by this host
	# re-uses 'allow' chain from main.nft
	chain FORWARD {
		type filter hook forward priority filter + 20
		policy accept

		jump allow
		reject with icmpx type host-unreachable
	}
}

Alas, that uses chain “allow” defined in the main.nft.
If one wants to allow absolutely everything to pass between LAN and WAN, then:

table inet nftables_svc {
	chain FORWARD {
		type filter hook forward priority filter + 20
		policy accept
	}
}

Starting from FirewallD’s chain, one could use:

table inet nftables_svc {
	chain FORWARD {
		type filter hook forward priority 0; policy accept;
		ct state {established, related} accept
		iif lo accept
		iifname "lan" accept
		iifname "wan" counter accept
		ct state invalid counter drop
		counter reject with icmp type host-prohibited
	}
}

The ‘counter’ is optional – it adds packet counter so we see how many packets have entered the rule.

The names of interfaces used in the rules should be such that they are predictable. The traditional names ethN names are not, unless NetworkManager binds connection to MAC address and then (re)names the interface.

Try use ShoreWall

Shorewall appears to be dead since Sept 2020 so that’s a no go.

Back on track: are there speed tweaks that need to be done to this VM? When I have this router in place, max speed I get across it is 460M on a 1G fiber circuit. When I have the router itself speed test it routinely tests at 970M. So any traffic crossing it is slowed by 50%. The only real solution I have is to just bite the bullet and get an L3 switch to do the basic static routing that I need.

Suggestions?

TuneD may have some pre-made profile Chapter 1. Getting started with TuneD | Red Hat Product Documentation

How minimal is your forward-related ruleset now?

Thanks. I tried TuneD and it doesn’t change anything. I’ve tried several different profiles related to network throughput and it doesn’t change anything. I always get around 460M when simple forwarding is done through this Linux router.

There are no forwarding rules. I’ve enabled forwarding in the kernel and SSH access (different port). All I had to do was enable forwarding in the sysctl file and reboot and forwarding was auto enabled in firewalld.

RH has actually a chapter about network tuning too: Chapter 31. Tuning the network performance | Red Hat Product Documentation

The default in FirewallD is to deny all forwarding, even when it is enabled in kernel (with the sysctl).

Ideally, there would be no rules, no chain, on type filter hook forward as you want everything to pass through with minimal effort.


Rocky 9 has also something called “extended Berkeley Packet Filter (eBPF)” Chapter 42. Understanding the eBPF networking features in RHEL 9 | Red Hat Product Documentation
but I have no idea whether its “to complement or replace kernel packet processing” can allow faster unfiltered forwarding.
[EDIT] Looks like it can: GitHub - Nat-Lab/xdp-router: a bare-minimum XDP router implementation with basic IPv4 and IPv6 support