I’ve been writing about setting up a FreeBSD-based eXO router, first the upstream and PPPoE details were covered, then how to setup the gateway, access point and an IPv6-only network. This builds upon a system that can properly route IPv6 and IPv4 as well as has a means to connect other clients and possibly providing them with an IPv6 address and route.
The goal here is to setup the bits of the router needed for everyday usage.
This is part of a series on home routing with FreeBSD.
As mentioned on the first post, this setup is based on
an APU2d4, with three ethernet interfaces: igb0
for egress/wan, igb1
for
local devices and, igb2
as a management interface.
The current state is that the machine can setup the connection to eXO
and can correctly accept the IPv6 and IPv4 routes and delegations, furthermore
it creates an access point wlan0
that is bridged to igb1
(bridge is called
lair
) and any devices that connect to these interfaces are able to use only
the IPv6 internet.
Network Address Translation, one of those things that are secretly quite complex and a bit of a PITA to debug.
The Internet was made to be End-To-End, to have all members of the network be equal in the sense that they can provide and consume services the exact same way [old knowledge; citation needed].
NAT gets in the way of that and it was a necessary evil. Virtually all ISPs that provide you with an internet connection, will also assign you a single IPv4 address (either dynamically or statically), but you surely have more than one device at any given home/company location.
Since, except in very specific cases, it’s not valid for multiple devices to work with the same IP, NAT shows up everywhere.
In a nutshell, NAT makes sure that your 192.168.X.Y
IPv4 doesn’t leave
the local network (because it’s “non-routable”, aka shouldn’t leave the
premises) and then connects on your behalf to whatever other peers you
requested.
Then, when the answer comes back, it’s “smart enough” to know that that reply
has to go to your 192.168.X.Y
instead of to some 192.168.W.Z
in the
network.
That’s trickier than it sounds and indeed means that, without some kind of permanent port-forwarding, it is not possible to reach devices behind the NAT for better and for worse.
Since RIPE ran out of IPv4 in 2019, this is specially important: now ISPs are starting to use Carrier-Grade NAT, some even “sell it” as a feature because it means they can give you a crappier internet at lower prices.
Crappier internet? Well, if you or someone you know/trust manage your NAT, it is possible to do things like port-forwarding, if in reality, your NAT is behind another NAT that hosts thousands of customers; there is no real option for that.
Random trivia: The Nintendo Switch will happily tell you how crappy your NAT is :-) on a scale from “A: mostly works” to “F: how did this happen?”. CGNAT will automatically place you somewhere between “C” and “D”.
So, eXO is in this regard only different, in that they assign a static IPv4 address to the connection that is directly addressable by any device connected to the IPv4 internet.
Which means, I do need NAT if I want IPv4 support.
Side note for some other time: NAT64 and DNS64 make it mostly unnecessary to have IPv4 (and NAT!) in the first place. This has been tested out successfully at length by many, but specially by the awesome people at ungleich.ch at last Hack4Glarus and with their IPv6 only hosting Virtual Private Server (VPS) offering.
Before actually translating addresses, we have to provide those addresses to devices in the network. That’s what DHCP is good for.
The along with the DHCP6 client (dhcp6c
), DHCP server is the second bit that
we need that is not part of the base system.
In this case, I’ll use OpenBSD‘s dhcpd
, which is packaged as a
port for FreeBSD.
As with dhcp6c
, installing dhcpd
is a matter of:
pkg install dhcpd
And the magical man pages are man 5 dhcpd.conf
and man 8 dhcpd
and, even if
it is focused on isc-dhcpd-server
, the
Chapter on DHCP from the FreeBSD Handbook.
Familiar pattern by now, setup /usr/local/etc/dhcpd.conf
(since dhcpd
is
not part of the base system, its config lives in /usr/local/etc
) as follows:
# Add a suffix for local name queries
option domain-name "evilham.local";
# TODO: change to own DNS. Using quad9 for now
option domain-name-servers 9.9.9.9, 114.112.112.112;
# Default to a 1 day lease
default-lease-time 86400;
# Limit the lease to, say a month if someone
# requests a longer lease.
max-lease-time 2592000;
# 2^16 should be enough addresses for everyone (' ^.^)
option subnet-mask 255.255.0.0;
# This will have to be the internal IPv4 of the router
option routers 192.168.0.1;
subnet 192.168.0.0 netmask 255.255.0.0 {
# I want to "reserve" some addresses for ${reasons}
range 192.168.77.0 192.168.99.255;
}
And setup /etc/rc.conf
:
# Modify this existing line to add the router's IPv4 and subnet
ifconfig_lair="inet 192.168.0.1/16 addm igb1 addm wlan0"
# Start giving out IPv4 addresses
dhcpd_enable="YES"
# And explicitly do that only locally
dhcpd_flags="lair"
Then start the dhcpd
service:
service dhcpd start
Now any device connected to the bridge interface will get an IPv4 address \o/.
BUT! We have no NAT yet, so, things will get weird soon! (*)
(*): they got weirder than expected,
ppp
was doing NAT?That doesn’t sound right :-D. Let’s trust
pf
more thanppp
for that job.Modified
/etc/ppp/ppp.conf
so that theexo
profile also contains a line:nat enable no
This should have been the default if I understood documentation properly.TODO: Figure out why this was the default behaviour and fix documentation/open a bug
Besides talking to our local network, these addresses are not good for anything. Since they are not routable, any routers must refuse to forward the packets any further.
FreeBSD has two main firewalls, I like pf
.
It originated in OpenBSD like so many great things, and was ported
to FreeBSD.
Kristof Provost’s talk on automated firewall testing, explains a bit how
it came to be that OpenBSD‘s pf
and FreeBSD‘s pf
are
actually different.
The TL;DR is: nobody has done the work to upgrade it, it works mostly fine, and
the diff is not as small as one would expect.
OpenBSD‘s pf
has a bunch of goodies, like support for NAT64
:-).
Hopefully things catch up soon.
Back to topic: man 4 pf
, man 5 pf.conf
and man 8 pfctl
are the magical
man pages.
And a super-tiny but permissive example on how to do NAT was already done by
Kamila here, so go steal that into /etc/pf.conf
.
With an important addition for dhcp6c
:
# TODO: update with provisioning example
# In practise, this should be limited to link local addresses
pass in quick proto udp to port dhcpv6-client
Now we have to apply those rules, since I was already loading pf
, albeit with
an empty set of rules, this means running:
service pf reload
And boom, now everything in the network can talk to the outside over IPv4.
pf