Commit 4144c37f authored by David Johnson's avatar David Johnson

Change and complete the systemd-networkd control net support for ubuntu 18.

Turned out that I could not guarantee that emulab-networkd@.service
would write the .network files in /run/systemd/network in time for
systemd-networkd to notice them; so I tweaked the strategy to a slightly
more complex path: udev rules run a simple shell script that writes the
per-device .network files that specify dhcp on all interfaces, in
addition to telling systemd to want emulab-networkd@<dev>.service units.
These units wait for the control net to come up on one of the devices;
then the winner removes the extraneous .network files and downs the
unused interfaces and restarts systemd-networkd so it only "manages" our
control net.

I also improved it so that if the user writes static systemd-networkd
configuration into /etc/systemd/network/<foo>.network, our udev rules
still write /run/systemd/network/<foo>.network if foo is a physical
device -- but the waiter unit and cleanup scripts do *not* down <foo>;
they just ignore it, because they notice that systemd-networkd did *not*
pick our /run/systemd/network/<foo>.network to manage <foo>.  So we
should be good for integration and not squashing static user configs.

Finally, I "completed" it in the sense that the winning
emulab-networkd@.service unit also writes out all the /var/emulab/boot
metadata, sets the hostname -- does what the dhclient exit hooks used to

The only thing it does not yet handle is elabinelab dhcp "selection".  I
need a quick semantics tutorial for that.
parent eb538be9
ACTION!="remove", SUBSYSTEM=="net", KERNEL!="lo", ENV{SYSTEMD_WANTS}+="emulab-networkd@$name.service"
ACTION!="remove", SUBSYSTEM=="net", KERNEL!="lo", ENV{SYSTEMD_WANTS}+="emulab-networkd@$name.service", RUN+="/usr/local/etc/emulab/ $name"
ACTION="remove", SUBSYSTEM=="net", KERNEL!="lo", RUN+="systemctl stop --no-block emulab-networkd@$name.service"
# This simply drops a .network file for interface $1 into
# /run/systemd/network so that networkd can pick it up, unless it gets
# overridden.
if [ -z "$iface" ]; then
echo "ERROR: must provide an interface as the sole argument"
exit 1
# NB, if the user has overridden this by some file in
# /etc/systemd/network, that takes precedence, and this won't be run.
# We have specific code in that is run from
# emulab-networkd-online@.service that checks to see if this file was
# used or not; we only handle the iface there if this file got used.
mkdir -p /run/systemd/network
cat <<EOF > /run/systemd/network/${iface}.network
Description=Emulab control net search on $iface
exit 0
if [ -z "$iface" ]; then
echo "ERROR: must provide an interface as the sole argument"
exit 1
if [ -e /etc/emulab/ ]; then
. /etc/emulab/
mkdir -p $LOGDIR
echo "`date`: ${iface}: starting" >>$LOGFILE 2>&1
while [ ! $found -eq 1 ]; do
# If the control net iface was found, and it's not us, exit.
if [ -e /run/cnet ]; then
echo "`date`: ${iface}: control net is not us" >>$LOGFILE 2>&1
exit 0
# The man page promises this will wait for $iface, but it does not.
# Once an interface comes up, it seems to return. Go figure. This
# means we have to check *which* interface came up; this is how we
# determine if it was our interface or not.
/lib/systemd/systemd-networkd-wait-online -i "$iface" --timeout 2
if [ $? -eq 0 ]; then
networkctl status "$iface" | grep -qi configured
if [ $? -eq 0 ]; then
echo "`date`: ${iface}: control net is us" >>$LOGFILE 2>&1
mkdir -p /run/emulab
echo "$iface" > /run/cnet
# If the control net iface was us, *and* if systemd-networkd tried to
# use the .network files our udev helper generated (we have to be
# careful that user did not override them with static configuration),
# remove the .network files other instances of us created, down those
# ifaces (systemd-networkd leaves them up), and restart
# systemd-networkd, so it properly shows that it is only managing the
# one control net interface. We could kill it off, but may as well
# leave it running; running `networkctl` will bring it right back. It
# is less invasive than networkmanager, so this should be fine.
if [ $found -eq 1 ]; then
echo "emulab-networkd[$$]: found $iface as control net"
controlif=`cat /run/cnet`
for file in `ls -1 /run/systemd/network/*.network | grep -v $`; do
grep -q "Description=.*Emulab" $file
if [ ! $? -eq 0 ]; then
echo "`date`: ${iface}: $file is not ours; ignoring" >>$LOGFILE 2>&1
ifa=`echo $file | sed -ne 's|^.*/network/\([^\.]*\)\.network$|\1|p'`
rm -f $file
# Check to see if our file was being used to manage this iface,
# or if it got overridden.
networkctl status $ifa | grep -q "Network File: $file"
if [ $? -eq 0 ]; then
ip link set $ifa down
echo "`date`: ${iface}: downed $ifa" >>$LOGFILE 2>&1
echo "`date`: ${iface}: our .network for $ifa was overridden; just removing our .network file" >>$LOGFILE 2>&1
# Restart systemd-networkd so its management status as shown via
# networkctl is correct; we manage the other ifaces.
echo "`date`: ${iface}: restarting systemd-networkd" >>$LOGFILE 2>&1
systemctl restart systemd-networkd
# Sadly, this does not get the back into the
# alive state, but we try; it was already hit, and restarting
# systemd-networkd inactivates it. But this seems to cause no
# systemic harm.
systemctl restart systemd-networkd-wait-online
# Write some metadata in /var/emulab/boot. The
# /run/systemd/netif/leases/<ifindex> file has what we need.
mkdir -p $BOOTDIR
ifindex=`cat /sys/class/net/$iface/ifindex`
if [ -e /run/systemd/netif/leases/$ifindex ]; then
echo "`date`: ${iface}: writing cnet metadata in $BOOTDIR" >>$LOGFILE 2>&1
. /run/systemd/netif/leases/$ifindex
elif [ -e /run/systemd/netif/leases/* ]; then
echo "`date`: ${iface}: unexpected leases file with mismatched ifindex; attempting to use anyway to write cnet metadata in $BOOTDIR" >>$LOGFILE 2>&1
. /run/systemd/netif/leases/*
echo "`date`: ${iface}: no leases file in /run/systemd/netif/leases; cannot write cnet metadata in $BOOTDIR; failing!" >>$LOGFILE 2>&1
exit 1
# Actually do the metadata file writes.
echo $HOSTNAME > $BOOTDIR/realname
echo $ROUTER > $BOOTDIR/routerip
echo $ADDRESS > $BOOTDIR/myip
echo $NETMASK > $BOOTDIR/mynetmask
echo $DOMAINNAME > $BOOTDIR/mydomain
echo $iface > $BOOTDIR/controlif
# For Xen-based vnodes we record the vnode name where the scripts expect it.
# XXX this works because only Xen-based vnodes DHCP.
case "$HOSTNAME" in
echo $HOSTNAME > $BOOTDIR/vmname
# Make sure the hostname is set to something sane.
if [ `hostname -s` != "$HOSTNAME" ]; then
hostname $HOSTNAME
# Ensure /etc/resolv.conf is set to something sane. We are not using
# systemd-resolved, so all we want is for /etc/resolv.conf to point to
# /run/systemd/resolve/resolv.conf . But if it doesn't, just update it
# in place.
if [ -L /etc/resolv.conf \
-a ! -f `readlink -f /etc/resolv.conf` \
-f /run/systemd/resolve/resolv.conf ]; then
echo "`date`: fixing /etc/resolv.conf to point to /run/systemd/resolve/resolv.conf" >>$LOGFILE 2>&1
ln -sf /run/systemd/resolve/resolv.conf /etc/resolv.conf
elif [ ! -L /etc/resolv.conf ]; then
echo "`date`: updating static /etc/resolv.conf; should be symlink!" >>$LOGFILE 2>&1
rm -f /etc/resolv.conf
echo nameserver $DNS > /etc/resolv.conf
echo search $DOMAINNAME >> /etc/resolv.conf
# See if the Testbed configuration software wants to change the hostname.
if [ -x $BINDIR/sethostname.dhclient ]; then
$BINDIR/sethostname.dhclient >>$LOGDIR/dhclient.log 2>&1
echo "`date`: ${iface}: done!" >>$LOGFILE 2>&1
exit 0
Description=Emulab systemd-networkd on %I
ExecStart=/bin/sh -c 'mkdir -p /run/systemd/network; /bin/echo -ne "[Match]\nName=%I\n[Network]\nDHCP=yes\n[DHCP]\nCriticalConnection=yes\nUseNTP=yes\nUseHostname=yes\nUseDomains=yes\n" > /run/systemd/network/; found=0; while [ ! $found -eq 1 ]; do if [ -e /run/emulab/cnet ]; then exit 0; fi; /lib/systemd/systemd-networkd-wait-online -i %i --timeout 2; if [ $? -eq 0 ]; then networkctl status %i | grep -qi configured; if [ $? -eq 0 ]; then mkdir -p /run/emulab; echo %i > /run/emulab/cnet; found=1; fi; fi; done; if [ $found -eq 1 ]; then echo "$$ found %i as control net"; controlif=`cat /run/emulab/cnet`; for file in `ls -1 /run/systemd/network/en*.network | grep -v $`; do rm -fv $file ; done; systemctl restart systemd-networkd; systemctl restart systemd-networkd-wait-online; fi; exit 0;'
ExecStart=/usr/local/etc/emulab/ %i
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment