#!/bin/bash # Source Qubes library. # shellcheck disable=SC1091 . /usr/lib/qubes/init/functions set -ueo pipefail add_link_route () { ip -- route replace to unicast "$1" dev "$2" scope link } add_default_route () { ip -- route replace to unicast default via "$1" dev "$2" onlink } readonly netvm_mac=fe:ff:ff:ff:ff:ff 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 custom="${11}" ip -- address replace "$ip/$netmask" dev "$INTERFACE" if [[ "$custom" = false ]]; then ip -- neighbour replace to "$gateway" dev "$INTERFACE" \ lladdr "$netvm_mac" nud permanent fi if [ -n "$ip6" ]; then ip -- address replace "$ip6/$netmask6" dev "$INTERFACE" if [ -n "$gateway6" ] && [[ "$custom" = false ]]; then ip -- neighbour replace to "$gateway6" dev "$INTERFACE" \ lladdr "$netvm_mac" nud permanent fi fi ip link set dev "$INTERFACE" group 1 up if [ -n "$gateway" ]; then add_link_route "$gateway" "$INTERFACE" if [ -n "$gateway6" ] && ! echo "$gateway6" | grep -q "^fe80:"; then add_link_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 # workaround systemd-resolved stupidity # https://github.com/systemd/systemd/pull/21317 if [ -h /etc/resolv.conf ]; then rm -f /etc/resolv.conf fi 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 if [ -x /usr/bin/resolvectl ] && \ systemctl is-enabled -q systemd-resolved.service && \ ! qsvc disable-dns-server ; then resolvectl dns "$INTERFACE" "$primary_dns" "$secondary_dns" 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 custom="${11}" local prefix local prefix6 local nm_config local ip4_nm_config local ip6_nm_config local uuid ip link set dev "$INTERFACE" group 1 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" || : if [[ "$custom" = false ]]; then ip -- neighbour replace to "$gateway" dev "$INTERFACE" \ lladdr "$netvm_mac" nud permanent fi if [ -n "$gateway6" ]; then if [[ "$custom" = false ]]; then ip -- neighbour replace to "$gateway6" dev "$INTERFACE" \ lladdr "$netvm_mac" nud permanent fi fi } 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 ret=0 /usr/lib/qubes/qubes-setup-dnat-to-ns || ret=$? [ "$ret" -eq 0 ] || [ "$ret" -eq 100 ] || exit "$ret" } 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 [ "$ACTION" == "add" ]; then MAC="$(get_mac_from_iface "$INTERFACE")" prefix="/net-config/$MAC/" if [[ -n "$MAC" ]]; then # prefix begins with / so -- is not needed if /usr/bin/qubesdb-read "${prefix}custom" >/dev/null 2>&1; then custom=true elif [[ "$?" = '2' ]]; then custom=false else echo "Could not check if ${prefix}custom exists!" >&2 exit 1 fi if ip4=$(exec /usr/bin/qubesdb-read "${prefix}ip" 2>/dev/null); then : elif [[ "$?" = '2' ]]; then prefix=/qubes- ip4=$(exec /usr/bin/qubesdb-read "${prefix}ip") else echo "Could not check if /net-config/$MAC/ip exists!" >&2 exit 1 fi netmask=$(exec /usr/bin/qubesdb-read --default=255.255.255.255 "${prefix}netmask") gateway=$(exec /usr/bin/qubesdb-read "${prefix}gateway") if ip6=$(exec /usr/bin/qubesdb-read "${prefix}ip6" 2>/dev/null); then netmask6=$(exec /usr/bin/qubesdb-read --default=128 "${prefix}netmask6") gateway6=$(exec /usr/bin/qubesdb-read --default="" "${prefix}gateway6") elif [[ "$?" != '2' ]]; then echo 'Could not check if IPv6 is enabled' >&2 exit 1 else ip6='' netmask6=128 gateway6='' 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= /lib/systemd/systemd-sysctl \ "--prefix=/net/ipv4/conf/all" \ "--prefix=/net/ipv4/neigh/all" \ "--prefix=/net/ipv6/conf/all" \ "--prefix=/net/ipv6/neigh/all" \ "--prefix=/net/ipv4/conf/$INTERFACE" \ "--prefix=/net/ipv4/neigh/$INTERFACE" \ "--prefix=/net/ipv6/conf/$INTERFACE" \ "--prefix=/net/ipv6/neigh/$INTERFACE" if [ -n "$ip4" ]; then # If NetworkManager is enabled, let it configure the network if qsvc network-manager && [ -e /usr/bin/nmcli ]; then configure_network_nm "$MAC" "$INTERFACE" "$ip4" "$ip6" "$netmask" "$netmask6" "$gateway" "$gateway6" "$primary_dns" "$secondary_dns" "$custom" else configure_network "$MAC" "$INTERFACE" "$ip4" "$ip6" "$netmask" "$netmask6" "$gateway" "$gateway6" "$primary_dns" "$secondary_dns" "$custom" 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