core-agent-linux/network/setup-ip
Marek Marczykowski-Górecki dd8de797e3
Move network uplink setup to a separate service
Previously, network uplink (eth0) was configured in two places:
 - udev (asynchronously)
 - qubes-misc-post.service - at the very end of the boot process

This caused multiple issues:
1. Depending on udev event processing (non-deterministic), network
   uplink could be enabled too early, for example before setting up
   firewall.
2. Again depending on udev processing, it can be enabled quite late in
   the boot process, after network.target is up and services assume
   network already configured. This for example causes qubes-firewall to
   fail DNS queries.
3. If udev happen try to enable enable networking even earlier, it may
   happend before qubesdb-daemon is started, in which case network setup
   fill fail. For this case, there was network re-setup in
   qubes-misc-post service - much later in the boot.

Fix the above by placing network uplink setup in a dedicated
qubes-network-uplink@${INTERFACE}.service unit ordered after
network-pre.target and pulled in by udev based on vif device existence,
to handle also dynamic network attach/detach.
Then, create qubes-network-uplink.service unit waiting for appropriate
interface-specific unit (if one is expected!) and order it before
network.target.

QubesOS/qubes-issues#5576
2020-12-04 03:24:02 +01:00

256 lines
7.7 KiB
Bash
Executable File

#!/bin/bash
# Source Qubes library.
# shellcheck disable=SC1091
. /usr/lib/qubes/init/functions
set -uo pipefail
add_host_route () {
/sbin/ip -- route replace to unicast "$1" dev "$2" onlink scope host
}
add_default_route () {
/sbin/ip -- route replace to unicast default via "$1" dev "$2" onlink
}
configure_network() {
local MAC="$1"
local INTERFACE="$2"
local ip="$3"
local ip6="$4"
local netmask="$5"
local netmask6="$6"
local gateway="$7"
local gateway6="$8"
local primary_dns="$9"
local secondary_dns="${10}"
local netvm_mac=fe:ff:ff:ff:ff:ff
/sbin/ifconfig "$INTERFACE" "$ip" netmask "$netmask"
/sbin/ip -- neighbour replace to "$gateway" dev "$INTERFACE" \
lladdr "$netvm_mac" nud permanent
if [ -n "$ip6" ]; then
/sbin/ifconfig "$INTERFACE" add "$ip6/$netmask6"
/sbin/ip -- neighbour replace to "$gateway6" dev "$INTERFACE" \
lladdr "$netvm_mac" nud permanent
fi
/sbin/ifconfig "$INTERFACE" up
if [ -n "$gateway" ]; then
add_host_route "$gateway" "$INTERFACE"
if [ -n "$gateway6" ] && ! echo "$gateway6" | grep -q "^fe80:"; then
add_host_route "$gateway6/$netmask6" "$INTERFACE"
fi
if ! qsvc disable-default-route ; then
add_default_route "$gateway" "$INTERFACE"
if [ -n "$gateway6" ]; then
add_default_route "$gateway6" "$INTERFACE"
fi
fi
fi
if [ -z "$primary_dns" ] && [ -n "$gateway" ]; then
primary_dns="$gateway"
fi
if ! is_protected_file /etc/resolv.conf ; then
echo > /etc/resolv.conf
if ! qsvc disable-dns-server ; then
echo "nameserver $primary_dns" > /etc/resolv.conf
echo "nameserver $secondary_dns" >> /etc/resolv.conf
fi
fi
}
configure_network_nm() {
local MAC="$1"
local INTERFACE="$2"
local ip="$3"
local ip6="$4"
local netmask="$5"
local netmask6="$6"
local gateway="$7"
local gateway6="$8"
local primary_dns="$9"
local secondary_dns="${10}"
local prefix
local prefix6
local nm_config
local ip4_nm_config
local ip6_nm_config
local uuid
prefix="$(get_prefix_from_subnet "$netmask")"
prefix6="$netmask6"
uuid="de85f79b-8c3d-405f-a652-${MAC//:/}"
nm_config="/etc/NetworkManager/system-connections/qubes-uplink-$INTERFACE"
cat > "$nm_config" <<__EOF__
[802-3-ethernet]
duplex=full
[ethernet]
mac-address=$MAC
[connection]
id=VM uplink $INTERFACE
uuid=$uuid
type=802-3-ethernet
__EOF__
ip4_nm_config=""
ip6_nm_config=""
if ! qsvc disable-dns-server ; then
ip4_nm_config="${ip4_nm_config}
dns=${primary_dns};${secondary_dns}"
fi
if ! qsvc disable-default-route ; then
ip4_nm_config="${ip4_nm_config}
addresses1=$ip;$prefix;$gateway"
if [ -n "$ip6" ]; then
ip6_nm_config="${ip6_nm_config}
addresses1=$ip6;$prefix6;$gateway6"
fi
else
ip4_nm_config="${ip4_nm_config}
addresses1=$ip;$prefix"
if [ -n "$ip6" ]; then
ip6_nm_config="${ip6_nm_config}
addresses1=$ip6;$prefix6"
fi
fi
if [ -n "$ip4_nm_config" ]; then
cat >> "$nm_config" <<__EOF__
[ipv4]
method=manual
may-fail=false
$ip4_nm_config
__EOF__
else
cat >> "$nm_config" <<__EOF__
[ipv4]
method=ignore
__EOF__
fi
if [ -n "$ip6_nm_config" ]; then
cat >> "$nm_config" <<__EOF__
[ipv6]
method=manual
may-fail=false
$ip6_nm_config
__EOF__
else
cat >> "$nm_config" <<__EOF__
[ipv6]
method=ignore
__EOF__
fi
chmod 600 "$nm_config"
# reload connection
nmcli connection load "$nm_config" || :
}
configure_qubes_ns() {
gateway=$(qubesdb-read /qubes-netvm-gateway)
#netmask=$(qubesdb-read /qubes-netvm-netmask)
primary_dns=$(qubesdb-read /qubes-netvm-primary-dns 2>/dev/null || echo "$gateway")
secondary_dns=$(qubesdb-read /qubes-netvm-secondary-dns)
echo "NS1=$primary_dns" > /var/run/qubes/qubes-ns
echo "NS2=$secondary_dns" >> /var/run/qubes/qubes-ns
/usr/lib/qubes/qubes-setup-dnat-to-ns
}
qubes_ip_change_hook() {
if [ -x /rw/config/qubes-ip-change-hook ]; then
/rw/config/qubes-ip-change-hook
fi
# XXX: Backward compatibility
if [ -x /rw/config/qubes_ip_change_hook ]; then
/rw/config/qubes_ip_change_hook
fi
}
have_qubesdb || exit 0
ACTION="$1"
INTERFACE="$2"
if [ -z "$INTERFACE" ]; then
echo "Missing INTERFACE argument" >&2
exit 1
fi
if [ -n "$INTERFACE" ]; then
if [ "$ACTION" == "add" ]; then
MAC="$(get_mac_from_iface "$INTERFACE")"
if [ -n "$MAC" ]; then
ip="$(/usr/bin/qubesdb-read "/net-config/$MAC/ip" 2> /dev/null)" || ip=
ip6="$(/usr/bin/qubesdb-read "/net-config/$MAC/ip6" 2> /dev/null)" || ip6=
netmask="$(/usr/bin/qubesdb-read "/net-config/$MAC/netmask" 2> /dev/null)" || netmask=
netmask6="$(/usr/bin/qubesdb-read "/net-config/$MAC/netmask6" 2> /dev/null)" || netmask6=
gateway="$(/usr/bin/qubesdb-read "/net-config/$MAC/gateway" 2> /dev/null)" || gateway=
gateway6="$(/usr/bin/qubesdb-read "/net-config/$MAC/gateway6" 2> /dev/null)" || gateway6=
# Handle legacy values
LEGACY_MAC="$(/usr/bin/qubesdb-read /qubes-mac 2> /dev/null)" || LEGACY_MAC=
if [ "$MAC" == "$LEGACY_MAC" ] || [ -z "$LEGACY_MAC" ]; then
if [ -z "$ip" ]; then
ip="$(/usr/bin/qubesdb-read /qubes-ip 2> /dev/null)" || ip=
fi
if [ -z "$ip6" ]; then
ip6="$(/usr/bin/qubesdb-read /qubes-ip6 2> /dev/null)" || ip6=
fi
if [ -z "$gateway" ]; then
gateway="$(/usr/bin/qubesdb-read /qubes-gateway 2> /dev/null)" || gateway=
fi
if [ -z "$gateway6" ]; then
gateway6="$(/usr/bin/qubesdb-read /qubes-gateway6 2> /dev/null)" || gateway6=
fi
fi
if [ -z "$netmask" ]; then
netmask="255.255.255.255"
fi
if [ -z "$netmask6" ]; then
netmask6="128"
fi
primary_dns=$(/usr/bin/qubesdb-read /qubes-primary-dns 2>/dev/null) || primary_dns=
secondary_dns=$(/usr/bin/qubesdb-read /qubes-secondary-dns 2>/dev/null) || secondary_dns=
if [ -n "$ip" ]; then
/sbin/ethtool -K "$INTERFACE" sg off
/sbin/ethtool -K "$INTERFACE" tx off
# If NetworkManager is enabled, let it configure the network
if qsvc network-manager && [ -e /usr/bin/nmcli ]; then
configure_network_nm "$MAC" "$INTERFACE" "$ip" "$ip6" "$netmask" "$netmask6" "$gateway" "$gateway6" "$primary_dns" "$secondary_dns"
else
configure_network "$MAC" "$INTERFACE" "$ip" "$ip6" "$netmask" "$netmask6" "$gateway" "$gateway6" "$primary_dns" "$secondary_dns"
fi
network=$(qubesdb-read /qubes-netvm-network 2>/dev/null) || network=
if [ -n "$network" ]; then
if ! qsvc disable-dns-server; then
configure_qubes_ns
fi
qubes_ip_change_hook
fi
fi
fi
elif [ "$ACTION" == "remove" ]; then
# make sure network is disabled, especially on shutdown, to prevent
# leaks when firewall will get stopped too
ip link set "$INTERFACE" down 2>/dev/null || :
# If exists, we delete NetworkManager configuration file to prevent duplicate entries
nm_config="/etc/NetworkManager/system-connections/qubes-uplink-$INTERFACE"
rm -rf "$nm_config"
else
echo "Invalid action '$ACTION'" >&2
exit 1
fi
fi