One of my favorite features of Fedora 22 is systemd-networkd and all of the new features that came with it in recent systemd versions. The configuration files are easy to read, bridging is simple, and tunnels are resilient.
I’ve recently started using a small Linux server at home again as a network router and firewall. However, I used systemd-networkd this time and had some great results. Let’s get started!
Overview
Our example router in this example has two network interfaces:
- eth0: public internet connectivity
- eth1: private LAN (192.168.3.1/24)
We want machines on the private LAN to route their traffic through the router to the public internet via NAT. Also, we want clients on the LAN to get their IP addresses assigned automatically.
Network configuration
All of the systemd-networkd configuration files live within
and we need to create that directory:
mkdir /etc/systemd/network
We need to write a network configuration file for our public interface that systemd-networkd can read. Open up
and write these lines:
[Match] Name=eth0 [Network] Address=PUBLIC_IP_ADDRESS/CIDR Gateway=GATEWAY DNS=8.8.8.8 DNS=8.8.4.4 IPForward=yes
If we break this configuration file down, we’re telling systemd-networkd to apply this configuration to any devices that are called
. Also, we’re specifying a public IP address and CIDR mask (like /24 or /22) so that the interface can be configured. The gateway address will be added to the routing table. We’ve also provided DNS servers to use with systemd-resolved (more on that later).
I added
so that systemd-networkd will automatically enable forwarding for the interface via sysctl. (That always seems to be the step I forget when I build a Linux router.)
Let’s do the same for our LAN interface. Create this configuration file and store it as
:
[Match] Name=eth1 [Network] Address=192.168.3.1/24 IPForward=yes
We don’t need to specify a gateway address here because this interface will be the gateway for the LAN.
Prepare the services
If we’re planning to use systemd-networkd, we need to ensure that it runs instead of traditional network scripts or NetworkManager:
systemctl disable network systemctl disable NetworkManager systemctl enable systemd-networkd
Also, let’s be sure to use systemd-resolved to handle our
:
systemctl enable systemd-resolved systemctl start systemd-resolved rm -f /etc/resolv.conf ln -s /run/systemd/resolve/resolv.conf /etc/resolv.conf
Reboot
We’re now set to reboot! It’s possible to bring up systemd-networkd without rebooting but I’d rather verify with a reboot now than get goosed with a broken network after a reboot later.
Once your router is back up, run
and verify that you have routable in the output for both interfaces:
[root@router ~]# networkctl IDX LINK TYPE OPERATIONAL SETUP 1 lo loopback carrier unmanaged 2 eth0 ether routable configured 3 eth1 ether routable configured
DHCP
Now that both network interfaces are online, we need something to tell our clients about the IP configuration they should be using. There are plenty of good options here, but I prefer dnsmasq. It has served me well over the years and it provides some handy features along with DHCP, such as DNS caching, TFTP and IPv6 router announcements.
Let’s install dnsmasq and enable it at boot:
dnf -y install dnsmasq systemctl enable dnsmasq
Open
in your favorite text editor and edit a few lines:
- Uncomment
dhcp-authoritative
- This tells dnsmasq that it’s the exclusive DHCP server on the network and that it should answer all requests
- Uncomment
interface=
and add
eth1on the end (should look like
interface=eth1when you’re done)
- Most ISP’s filter DHCP replies on their public networks, but we don’t want to take chances here. We need to restrict DHCP to our public interface only.
- Look for the
dhcp-range
line and change it to
dhcp-range=192.168.3.50,192.168.3.150,12h - We’re giving clients 12 hour leases on 192.168.3.0/24
Save the file and start dnsmasq:
systemctl start dnsmasq
Firewall
We’re almost done! Now it’s time to tell iptables to masquerade any packets from our LAN to the internet. But wait, it’s 2015 and we have tools like
to do that for us in Fedora.
Let’s enable masquerading, allow DNS, and allow DHCP traffic. We can then make the state permanent:
firewall-cmd --add-masquerade firewall-cmd --add-service=dns --add-service=dhcp firewall-cmd --runtime-to-permanent
Testing
Put a client machine on your LAN network and you should be able to ping some public sites from the client:
[root@client ~]# ping -c 4 icanhazip.com PING icanhazip.com (104.238.141.75) 56(84) bytes of data. 64 bytes from lax.icanhazip.com (104.238.141.75): icmp_seq=1 ttl=52 time=69.8 ms 64 bytes from lax.icanhazip.com (104.238.141.75): icmp_seq=2 ttl=52 time=69.7 ms 64 bytes from lax.icanhazip.com (104.238.141.75): icmp_seq=3 ttl=52 time=69.6 ms 64 bytes from lax.icanhazip.com (104.238.141.75): icmp_seq=4 ttl=52 time=69.7 ms --- icanhazip.com ping statistics --- 4 packets transmitted, 4 received, 0% packet loss, time 3005ms rtt min/avg/max/mdev = 69.659/69.758/69.874/0.203 ms
Extras
If you need to adjust your network configuration, just run
afterwards. I’ve found that it’s quite intelligent about the network devices and it won’t reconfigure anything that hasn’t changed.
The
command is very powerful. Check out the
and
functions to get more information about your network devices and the networks they’re connected to.
When something goes wrong, look in your systemd journal:
[root@router ~]# journalctl -u systemd-networkd -- Logs begin at Fri 2015-07-31 01:22:38 UTC, end at Fri 2015-07-31 02:11:24 UTC. -- Jul 31 01:46:14 router systemd[1]: Starting Network Service... Jul 31 01:46:14 router systemd-networkd[286]: Enumeration completed Jul 31 01:46:14 router systemd[1]: Started Network Service. Jul 31 01:46:15 router systemd-networkd[286]: eth1 : link configured Jul 31 01:46:15 router systemd-networkd[286]: eth0 : gained carrier Jul 31 01:46:15 router systemd-networkd[286]: eth0 : link configured Jul 31 01:46:16 router systemd-networkd[286]: eth1 : gained carrier
Kostic
Great article! I think that Fedora router will be my next project.
Major Hayden
Thanks, Kostic! Making a Linux router is fun. π
JR Swartz
Major Hayden, I ran through your procedures and it went flawless. Great article. However, I did notice that once I ran through your steps (I used 192.168.1.1 as my test interal nic), I could not change that IP once I took the router to its operational location. If I change that IP to, say 192.168.1.233, it no longer NATs for the client computers. Any thought?
Rabin
Nice guide, can this be extended to support multi ISP’s ?
and have some balancing and fail-over options ?
Thanks,
Major Hayden
Rabin — You could do multiple uplinks via some scripts. For example, you could try pinging the gateway addresses on both links and run scripts when one of them stops pinging for a certain amount of time. I’m not aware of anything built into systemd-networkd to do that, though.
Ivan
Forgot to close HTML tag in previous reply:
systemd.netdevhas it listed under “Supported kinds of virtual network devices” and I believe it is just what he needs.
anon
How did you get a network interface with the old naming style, i.e. eth0? Does it persist across reboots?
Major Hayden
Anon — In this particular case, I was using a virtual machine. That’s why the network devices showed up as eth0/eth1. To learn more about predictable network names, read my post on that subject:
https://major.io/2015/08/21/understanding-systemds-predictable-network-device-names/
Steven M. Miano
Great write up! It’d be awesome to see a follow up with multiple ISP links (having one internal network load balanced across multiple WANs, or multiple internal networks using PBR between multiple providers).
Excellent primer on configuring a basic home router and firewall!
Major Hayden
Thanks, Steven! I’ll add that to my list. π
Sonny
Bookmarked!
Thanks, very helpful
Major Hayden
You’re welcome, Sonny!
Roger
This would be su interesting project with the Banana Pl router. Are VLANs also supported here?
Major Hayden
Roger — You can do VLAN’s and other complicated network stuff, like bonded interfaces and vxlan. I wrote a post on this topic here:
https://major.io/2015/08/21/using-systemd-networkd-with-bonding-on-rackspaces-onmetal-servers/
Ryan Lerch
A user over on the G+ posting for this post asked an interesting question too — is there a webGUI availble to control systemd-networkd?
https://plus.google.com/+fedora/posts/7mkPcgj6UPS
Major Hayden
I’m not aware of any GUI’s of any sort for systemd-networkd. There’s always room for one! π
Lee Yates
Great article, thanks! I’ve tinkered with pfSense, IPFire and others in the past and am currently speccing an AM1 (Kabini) build as a firewall/router appliance for our home network (300Mbps WAN). Judging by your article setting up a router on Fedora is now even easier than setting up pfSense with a GUI! It would be fairly trivial to then set up openvpn and iptables to also have the box function as a VPN gateway (with network split across LAN interfaces if necessary). Nice! Thanks for giving me something else to tinker with. π
PaweΕ
Nice article. I really like how Fedora Magazine adds useful tutorials alongside news. Please keep them coming! π
Cameron
Great article, I have just tried to to follow this on the newly released clean install of fc23.
I however run into an issue where, /etc/resolv.conf pointing to /run/systemd/resolve/resolv.conf would create a permission issue for dnsmasq.
Many-thanks.
It would also be great to have a guide on how to do IPv6 routing in the full systemd world.
Major Hayden
Hey Cameron,
There’s a problem with the latest SELinux policies and they don’t allow dnsmasq to read /etc/resolv.conf (which is linked to /run/systemd/resolve/resolv.conf). I’m planning to get a bug open today to get it fixed.
Major
Andrew
I’m seeing this too. It seems dnsmasq wants to write to /etc/resolv.conf. It also wants to set an inotify which may take execute permissions. I’m not sure of that. This is FC 22.
Andrew
If I start dnsmasq from the command prompt: dnsmasq -q -d
Then it works. I’ve confirmed by watching a DHCPREQUEST work.
MichaΕ Kowalcze
Check your /lib/systemd/system/dnsmasq.service file and change
After=network.target
to:
After=systemd-resolved.service network.target
It helped in my case.
If works – perhaps main guide could be updated to include this change?
TonyBoston
Hi and thanks for the great Post.
Anyway, I ran into an issue here. After setting up the services and reboot the machine, theres only an ems3 interface and I cant see eth0-eth2 anywhere. I am running F23 server in a virtual machine.
Do you have any advice on that one?
Thanks,
Tony
Paul W. Frields
@TonyBoston: You’ll want to add another network device to the virtual machine using the manager, so you can experiment with two network interfaces. The naming is due to the virtual hardware being detected. You should be able to use the same steps and substitute the names provided on your VM.
Andrew
You might need to tweak your startup order. I added to dnsmasq.service:
[Unit]
After=network.target
So far I haven’t had any more problems with dnsmasq starting up before the resolve.conf file is written.
Francisco Franco
Thanks for a great write up. I used it for an Arch Wifi Router project and your systemd-networkd instructions are more accurate than Arch’s own Router and Software Access Point documentation. I even setup webmin for remote administration.
AWESOME
Skywalker
For those of you having trouble with the resolve.conf file, I found a solution that works great.
Open up your /etc/dnsmasq.conf file, and file and find the following line:
#server=/localnet/192.168.0.1
Below that line, add the following values, of course adjusting the IP addresses to your needs. I will be using google dns.
server=8.8.8.8
server=8.8.4.4
Simply save the file, and reboot your router, and you should not have any troubles. I’ve left my resolve.conf file with those settings as well, but in the event there are issues with dnsmasq looking for that file, you’ve now hard coded the dns entries in the dnsmasq daemon.
Cheers
Sam Mingo
Any ideas where you’re suppose to stash policy routing rules that you’d typically create with ip rule add … in systemd and/or systemd-networkd world?
Gabriel
I have a modem configured in bridged mode by my ISP, how can I configure fedora to use my public static IP?
Kent Brown
Is there a way to do this while having DHCP on a different server?