Bringing Up NetworkManager

This article is about bringing up various facilities using Network Manager as one changes networks. We've already covered how to bring down systemd-timesyncd and samba. Bringing those and other things up is the subject of this article.

As usual, the script, 50ifupdown, lives in /etc/NetworkManager/dispatcher.d.

I started this before NetworkManager provided the pre-down.d and pre-up.d directories, and code to support them. Refactoring this script to take advantage of those would probably be a good idea, and is left to the student.

There are three possible states in which NetworkManager will come up, at least on my laptops. They are:

  • WiFi and at home. We are at home on a home network. Set up our home services.

  • WiFi and not at home. We are on a foreign network. Shut down (or don't bring up) any home services.

  • Ethernet. In that case assume we are on a foreign network. Shut down (or don't bring up) any home services.

The home services consist of Samba, Syncthing, our home Debian package proxy server, a DNS server, bind9, and a time server. Also, there are two firewalls defined, one for home and one for foreign networks.

The home network's Debian package proxy is indicated by the file 02proxy, which normally lives in /etc/apt/apt.conf.d/. However, I make it a file in /etc/apt/. When at home, it is active, via a symlink in /etc/apt/apt.conf.d/. You will see how we use that in lines 113-116, 131-134, and 169-172.

We detect the state for which we are setting up by examining our environment when the script runs.

Another problem is that I am lazy (in Larry Wall's sense). I want one script to run on several laptops. So I have to detect the wired and wireless interfaces at run time. This is what the variables WIREDUP, WIREDDOWN, WIRELESSUP, and WIRELESSDOWN are for. Those are set up in lines 41-45.

Another aspect of laziness is that I want the script to run successfully whether a given service is present on the laptop or not. So we test to be sure that one is present before we do anything with it.

We get the action NetworkManager is executing by examining our command line, and preserve it in the variable ACTION.

Setting up the home ESSIDs in an array might be more elegant than the current setup. We will leave that as an exercise for the student.

Most of the script is a big case statement based on the action to be taken. The two most significant are 'up' and 'down'. Within those two, we determine the network we are on, and act accordingly. One thing we don't act on is VPNs, since I don't use one.

We provide a log for debugging. Also, NetworkManager logs to var/log/syslog, so you should watch both of those when debugging this script.

#!/bin/bash

# Custom actions for NetworkManager. This should run *after*
# 01ifupdown, so assume that's all been done. See "man NetworkManager"
# for the gory details.

# Virtual machines use bind. So we need it up regardless of the
# network. Bind comes up during normal startup. But since NM isn't
# running yet, we don't have a network. So bind comes up only
# listening to the loopback device, which doesn't help other
# computers. So we restart it.

# Emacs (and likely other editors) can create backup files, indicated
# by appending a tilde (~) to the file name. We don't want those to
# execute, but NM will blindly execute them. So we silently refuse to
# run if the last character in the name of the file is a tilde.

/bin/echo "${0}" | grep --quiet ~$ && exit 0

# Fake ifupdown environment
export IFACE="$1"
export LOGICAL="$1"
export ADDRFAM="NetworkManager"
export METHOD="NetworkManager"
export VERBOSITY="0"

ACTION=$2
ESSID=$(/sbin/iwgetid "${IFACE}" --raw)

# Our home samba mount point.
smbMount='/home/charles/samba'

# Specify our interfaces. Look at any ethx or wlanx. Debian on the
# T520 used to come up with wlan1, not wlan0. Go figure.

# We also support "Predictable Network Interface Names". See
# https://www.freedesktop.org/wiki/Software/systemd/PredictableNetworkInterfaceNames/
# and the code, at
# https://github.com/systemd/systemd/blob/master/src/udev/udev-builtin-net_id.c#L20

# Dragon: enp2s8, iorich, orca: enp0s25, jhegaala: eth0, personal
# hotspot: enp0s29v1v1c4i2
WIREDUP=$(ifconfig | grep -E '^e(th|n).*RUNNING' | cut -f1 -d:)
WIREDDOWN=$(ifconfig | grep -E '^e(th|n)' | cut -f1 -d:)

# dragon: wlp2s2,       iorich, orca: wls3,     jhegaala: wlp3s0
WIRELESSUP=$(ifconfig | grep -E '^wl(an)?.*RUNNING' | cut -f1 -d:)
WIRELESSDOWN=$(ifconfig | grep -E '^wl(an)?' | cut -f1 -d:)

LOG=/var/log/NetworkManager
HOMEESSID1=CurleyNet314159      # One home essid
HOMEESSID2=Curleynet2           # Another home essid
HOMEESSID3=Curleynet5           # Yet another home essid

# HOMEESSID=curleynet             # Our home essid

FOREIGNFIREWALL='/etc/firewalls/shorewall.foreign'
HOMEFIREWALL='/etc/firewalls/shorewall.home'


# per https://www.theregister.co.uk/2019/06/17/linux_tcp_sack_kernel_crash/
echo 0 > /proc/sys/net/ipv4/tcp_sack

# If we have syncthing, set the name of the service to run.
if [ -x /usr/bin/syncthing ] ; then
    SYNCTHINGSERVICE=syncthing@charles
fi

{ /bin/echo >> $LOG 2>&1
  /bin/date
  /bin/echo \$@ arguments: "$@"
  /bin/echo ESSID is: "${ESSID}"
} >> $LOG 2>&1


if [ "$ACTION" = 'connectivity-change' ] ; then
    echo Action is "${ACTION}". >> "${LOG}" 2>&1
    exit 0
fi

# Bail if it is not an interface we are interested in, like one for
# the virtual machines. Or if it's an interface that's already shut
# down.

if [ "${IFACE}" != "$WIREDUP" ] \
            && [ "${IFACE}" != "$WIRELESSUP" ] \
            && [ "${IFACE}" != "$WIREDDOWN" ] \
            && [ "${IFACE}" != "$WIRELESSDOWN" ]
then
    { echo "${IFACE}" is not an interface of interest
      ifconfig "${IFACE}"
      route -n
    } >> "${LOG}" 2>&1
    exit 0
fi

case $ACTION in

    'up' )
        # /bin/echo Running UP code. IFACE is "${IFACE}" >> "${LOG}" 2>&1

        if [ "${IFACE}" = "$WIREDUP" ] ; then
            /bin/echo "Wired Interface $WIREDUP." >> "${LOG}" 2>&1

            if [ -x /etc/init.d/bind9 ] ; then
                /etc/init.d/bind9 restart >> "${LOG}" 2>&1
            fi

            # Use an appropriate firewall
            echo $FOREIGNFIREWALL start >> "${LOG}" 2>&1
            $FOREIGNFIREWALL start >> "${LOG}" 2>&1

            # We can't use our home repo cache
            if [ -e /etc/apt/apt.conf.d/02proxy ] ; then
                rm /etc/apt/apt.conf.d/02proxy
            fi

        else

            /bin/echo "Wireless Interface." >> "${LOG}" 2>&1
            if [ "${ESSID}" = "$HOMEESSID1" ] ||
                   [ "${ESSID}" = "$HOMEESSID2" ] ||
                   [ "${ESSID}" = "$HOMEESSID3" ]; then

                /bin/echo "Home network" >> "${LOG}" 2>&1

                if [ -x /etc/init.d/bind9 ] ; then
                    /etc/init.d/bind9 restart >> "${LOG}" 2>&1
                fi

                # Use our home repo cache
                if [ ! -e /etc/apt/apt.conf.d/02proxy ] ; then
                    ln -s /etc/apt/02proxy /etc/apt/apt.conf.d/02proxy ;
                fi

                echo $HOMEFIREWALL start >> "${LOG}" 2>&1
                $HOMEFIREWALL start >> "${LOG}" 2>&1

                # Syncthing only runs on the home wireless network
                if [ -x /usr/bin/syncthing ] ; then
                    if [ "$(systemctl status ${SYNCTHINGSERVICE} \
                             | grep 'Active: active' >> /dev/null ; echo $?)" \
                         = '0' ] ; then
                        echo Syncthing is already running.  >> "${LOG}" 2>&1
                    else
                        # Start it up.
                        echo Starting Syncthing. >> "${LOG}" 2>&1
                        systemctl start ${SYNCTHINGSERVICE} >> "${LOG}" 2>&1
                    fi
                fi

                if command -v mount.cifs  >> /dev/null ; then
                    echo Mounting the Samba mount... >> "${LOG}" 2>&1
                    mount $smbMount >> "${LOG}" 2>&1
                fi

            else

                /bin/echo "Foreign network" >> "${LOG}" 2>&1

                if [ -x /etc/init.d/bind9 ] ; then
                    /etc/init.d/bind9 restart >> "${LOG}" 2>&1
                fi

                # Use an appropriate firewall
                echo $FOREIGNFIREWALL start >> "${LOG}" 2>&1
                $FOREIGNFIREWALL start >> "${LOG}" 2>&1

                # We can't use our home repo cache
                if [ -e /etc/apt/apt.conf.d/02proxy ] ; then
                    rm /etc/apt/apt.conf.d/02proxy
                fi

            fi

        fi

        ifconfig "${IFACE}" >> "${LOG}" 2>&1
        route -n >> "${LOG}" 2>&1

        ;;

    'down' )
        /bin/echo "Lost network" >> "${LOG}" 2>&1

        if [ -x /etc/init.d/bind9 ] ; then
            /etc/init.d/bind9 restart >> "${LOG}" 2>&1
        fi

        if [  "${IFACE}" = "$WIRELESSDOWN" ] ; then

            case $HOSTNAME in
                'dragon' )

                    DEVICE=ipw2200;
                    ;;

                'jhegaala' )
                    DEVICE=rtl8192ce;
                    ;;

                'orca' | 'iorich')
                    DEVICE=iwl4965;
                    ;;

                * )
                    DEVICE='Bad device! Please fix!' >> "${LOG}" 2>&1
            esac

            echo Reloading device "${DEVICE}". >> "${LOG}" 2>&1
            rmmod "${DEVICE}" && modprobe "${DEVICE}" >> "${LOG}" 2>&1

            # reload flakey proprietary bits!
            # rmmod ipw2200 && modprobe ipw2200 # R51 dragon
            # rmmod rtl8192ce && modprobe rtl8192ce # t520: jhegaala
            # rmmod iwl4965 && modprobe iwl4965     # t61: orca, iorich
        fi

        ;;

    'dhcp4-change' )

        if [ "${ESSID}" = "$HOMEESSID1" ] ||
               [ "${ESSID}" = "$HOMEESSID2" ] ||
               [ "${ESSID}" = "$HOMEESSID3" ]; then

            echo $HOMEFIREWALL start >> "${LOG}" 2>&1
            $HOMEFIREWALL start >> "${LOG}" 2>&1

        else

            # Use an appropriate firewall
            echo $FOREIGNFIREWALL start >> "${LOG}" 2>&1
            $FOREIGNFIREWALL start >> "${LOG}" 2>&1

        fi
        ;;

    # Events we ignore.

    'pre-up' | 'dhcp6-change' | 'vpn-up' | 'vpn-down' \
        | 'hostname' | 'connectivity-change' | 'post-down' )

        ;;

    * )
        /bin/echo "!! Bad Input Action!!" >> "${LOG}" 2>&1
        ;;

esac

exit 0

blogroll

social