Administration

IPv4 Routing Table Basics

When developing a system with multiple IP interfaces, understanding where your data traffic is going to go is of utmost importance. For a single-homed system, that is a system with a single interface, this is very obvious. For a dual-homed system, that is a system with multiple interfaces, this can become complicated.

Before continuing, we will briefly discuss host configuration.

Host Configuration

Configuration of an Internet host (node/device) nominally consists of the following variables:

  1. Host address (“IP Address”) – the unique identifier of the host on the network
  2. Network mask – bitwise mask used to separate the network address from the host address
  3. Default gateway – IP address of the router to forward packets to for networks that a direct route to does not exist

Consider a typical home network with a laptop that has IP address 192.168.0.123 and netmask of 255.255.255.0. Using the netmask we can separate the network address from the host address by bitwise-ANDing the two. Converting the “human representation” of the IP address to a 32-bit hex value can make things a bit easier to understand.

192.168.0.123 -> 0xC0A8007B
255.255.255.0 -> 0xFFFFFF00

  C0A8007B
& FFFFFF00
----------
  C0A80000

Thus we have a network address of 0xC0A80000, or 192.168.0.0.

Another way to reflect the network mask is /<n> where <n> is the number of bits, from the left/most-significant-bit, that are set. For example, following the above, the network is 192.168.0.0/24.

0xFFFFFF00 -> 0b11111111111111111111111100000000 -> /24

IP Routing

Now that we understand the basics of host addresses and network addresses, we can look at routing.

The operating system uses the IP routing table to determine which interface to transmit data out. There are three primary pieces of information to be concerned with, all of which are similar to host configuration variables.

  1. Destination network – the address of the network in question
  2. Network mask – the network mask applied to target IP address to determine the destination network
  3. Default gateway – the IP of the router to forward packets to in case no direct route exists
  4. Network interface – the physical interface to use to transmit data out

Below is a routing table, somewhat abbreviated for explanation, from a Linux system using netstat -rn. This is a very common command, and available on nearly all Unix/Linux systems. More recent Linux systems may not install it by default and instead want you to use ip route show, which shows the same information. Personally, I prefer netstat given the tabular output of the data, and, if we are being completely honest, muscle memory.

~ $ netstat -rn
Kernel IP routing table
Destination     Gateway        Genmask         Interface
10.1.0.0        0.0.0.0        255.255.192.0   eth1
10.0.0.0        0.0.0.0        255.0.0.0       eth0
0.0.0.0         10.1.0.1       0.0.0.0         eth1

In this table, Genmask is synonymous with netmask. In this example, there are two networks and one default gateway.

  1. 10.1.0.0/255.255.192.0 on eth1
  2. 10.0.0.0/255.0.0.0 on eth0
  3. Any other network traffic is forwarded to 10.1.0.1

Routing Problem

Consider the host IP address 10.1.2.3. Which interface will it be forwarded out? eth0 or eth1?

There are two possible local networks to transit, with a default router at the end. First, let us investigate the eth1 route.

   10.  1.  2.3
& 255.255.192.0
---------------
   10.  1.  0.0     -> match on Destination variable

This is a match for the eth1 network, and therefore eth1 is a valid interface to sent traffic out.

For most home networks, things end here. However, this particular system has a second interface, eth0, which must also be consulted.

   10.1.2.3
& 255.0.0.0
---------------
   10.0.0.0         -> match on Destination variable

This is another valid route for the same host IP address!

When your application will send packets to 10.1.2.3, which interface will it use? eth0 or eth1? Technically, both are viable and provided your network is wired such that this is true, transmission should complete successfully. However, should physical wiring be such that they truly are separate networks, it could become the case that unicast traffic ingresses eth0 while corresponding traffic egresses eth1, or the other way around. Should eth1 not actually result in a path to the originating host, this will wreak havoc on the success of unicast network traffic.

Default Gateway

Now we will try to send a packet to 192.168.1.2.

  192.168.  1.2
& 255.255.192.0
---------------
  192.168.  0.0     -> no match for Destination variable on eth1

  192.168.1.2
& 255.  0.0.0
---------------
  192.  0.0.0       -> no match for Destination variable on eth0

As none of our directly connected networks are a match for this destination, the packet will be sent to the configured default gateway of 10.1.0.1.

Multicast Traffic

Multicast data traffic is no different, and needs a valid route. In most setups, this will simply be the default gateway. However, it could be that you have default gateway out one interface but wish multicast traffic to be sent out another interface. In this case, a static route is necessary.

route add -net 224.0.0.0 netmask 240.0.0.0 dev eth0

Installing Baikal on FreeBSD 13.1

This document contains instructions on installing Radicale on FreeBSD. It is aimed at a simple base install, and does not include reverse proxing or other hardening suitable for direct connection on the Internet.

All configuration in this guide is done as root.

Credits to this page for configuration file settings.

Installing FreeBSD

For this trial, I used FreeBSD 13.1 with all default settings during the installation process.

Installing Baikal

Configure pkg for us:

pkg update

Install Baikal:

pkg install www/baikal

Install nginx:

pkg install www/nginx

Open /usr/local/etc/nginx/nginx.conf, scroll down to the server section, and replace the entire section with:

server {
  listen         80 default_server;
  server_name    _;
 
  root           /usr/local/www/baikal/html;
  index          index.php;
 
  rewrite        ^/.well-known/caldav  /dav.php redirect;
  rewrite        ^/.well-known/carddav /dav.php redirect;
 
  charset utf-8;
 
  location ~ /(\.ht|Core|Specific|config) {
    deny all;
    return 404;
  }
 
  location ~ ^(.+\.php)(.*)$ {
    try_files $fastcgi_script_name = 404;
    include /usr/local/etc/nginx/fastcgi_params;
    fastcgi_split_path_info ^(.+\.php)(.*)$;
    fastcgi_pass unix:/var/run/php-fpm.sock;
    fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
    fastcgi_param PATH_INFO $fastcgi_path_info;
  }
}

Open /usr/local/etc/php-fpm.d/www.config and change the listen directive to:

listen = /var/run/php-fpm.sock

Then, uncomment the following lines:

listen.owner = www
listen.group = www
listen.mode = 0660

Correct file permissions for Baikal running as www:

chown -R www:www /usr/local/www/baikal/Specific /usr/local/www/baikal/config

Start FPM and nginx:

sysrc php_fpm_enable=YES
service php-fpm start
sysrc nginx_enable=YES
service nginx start

Finally, browse to the new install and follow the on-screen instructions to finalize configuration. For my testing, I simply used the sqlite backend.

Installing Radicale on FreeBSD 13.1

This document contains instructions on installing Radicale on FreeBSD. It is aimed at a simple base install, and does not include reverse proxing or other hardening suitable for direct connection on the Internet.

All configuration in this guide is done as root or sudo root.

Credits to this page for information and configuration file settings.

Installing FreeBSD

For this trial, I used FreeBSD 13.1 with all default settings during the installation process.

Installing Radicale

Configure pkg for us:

pkg update

Install Radicale:

pkg install www/radicale

Edit /usr/local/etc/radicale/config as follows below. Note that below is not the entire configuration file, only the relevant parts edited in the default file.

[server]
# Enable connections from anywhere. Later if putting this
# behind a reverse proxy, you would want to restrict it to
# localhost only.
hosts = 0.0.0.0:5232

[auth]
type = htpasswd
htpasswd_filename = /usr/local/etc/radicale/users
htpasswd_encryption = bcrypt

[rights]
type = owner_only

[storage]
filesystem_folder = /var/db/radicale/collections

Save the file and quit the editor.

Next, install Apache 2.4. We are really only doing this to get htpasswd, other approaches could be used such as py39-htpasswd, but that does not support bcrypt.

pkg install apache24

Next, create the password file and add any required users:

htpasswd -Bc /usr/local/etc/radicale/users firstuser
htpasswd -B /usr/local/etc/radicale/users subsequentuser

And set permissions on it so that the Radicale server can access it, but other logged in users cannot.

chown root:radicale /usr/local/etc/radicale/users
chmod 640 /usr/local/etc/radicale/users

And finally, start it:

sysrc radicale_enable=YES
service radicale start