Sometimes, it’s beneficial to be able to programatically tell from a client which uplink connection is being used by pfSense to route traffic, or simply have access to the current value of some property that maps to each respective uplink. This can be the case if, for example, there is a desire to pause certain network-intense activities running on a client when a metered, data-capped or lower-bandwidth uplink (for example mobile broadband) is in use.
Unfortunately, this information is not readily exposed in any way I have been able to find. However, it also isn’t that difficult to get at.
This post is aimed mainly at simple primary/backup multi-homed configurations, not load-balancing configurations or primary/backup load-balanced configurations. Some adjusting may be required if your multi-homed pfSense configuration includes load-balancing.
On FreeBSD (on which pfSense is based), the way to print the routing table is netstat -r -n
. Add an additional either -4
or -6
to print only the IPv4 or IPv6 routing table, respectively; by default, it prints both.
The uplink that at each time is being used by pfSense will typically be the default IP route. The default route, when printing the routing table through netstat -r -n
, will have a first field with the value default
.
To view the full output through the web interface, use Diagnostics > Command Prompt > Execute Shell Command. Be very careful; a typo or errant whitespace can be critical!
pfSense also includes awk
, which is quite handy for filtering table-like text output such as that produced by netstat
. We are primarily interested in the “Netif” (network interface) column of the output, for the line where the “Destination” field (the first one) has the value default
.
Log in to the administration interface. If you haven’t already installed the Cron package, do so first through System > Package Manager.
Once Cron is installed, go to Services > Cron > Settings, and add a new entry. The command to be executed should be something very similar to:
/usr/bin/netstat -rn4 | /usr/bin/awk '($1 == "default" && $4 == "mvnetaMM") { print "ONE" } ($1 == "default" && $4 == "mvnetaNN") { print "OTHER" }' >/usr/local/www/uplink.local.txt
This will write ONE
to /usr/local/www/uplink.local.txt
if the default route is through the interface mvnetaMM
, and will write OTHER
if the default route is through mvnetaNN
. The directory /usr/local/www
, in turn, is exposed to local clients as /
by the built-in administration interface web server.
You can add additional mappings (from physical interface name to an arbitrary value) on the same form if you have additional uplink interfaces. Look at Interfaces > Assignments in the administration web interface to see which physical interface name maps to which mnenomic name, and then from there decide what to expose if the default route is through that interface.
To avoid issues with quoting and encoding, I suggest only using US-ASCII alphanumeric characters in the awk
print
statements.
Do note that because Cron can only be configured to execute commands at a minute granularity, there will be a slight delay before a change in the default route is reflected in the file that is accessible from clients.
With the cron job in place, make the client request /uplink.local.txt
from the firewall (no authentication required!) and take whatever action is desired based on its contents, or the change in its contents. For example, on Linux, you might do:
wget -q -O - --no-check-certificate https://pfsense.home.arpa/uplink.local.txt
or
curl -s --insecure https://pfsense.home.arpa/uplink.local.txt
The --no-check-certificate
or --insecure
respectively is needed if the respective tool does not trust the TLS certificate for the pfSense host. If your client trusts the certificate, it’s better to remove that part.