Skip to content

Commit 4e79c76

Browse files
author
3np
committed
Merge '1cho1ce/add-ipv6-dnat-to-ns' into v4.3.27.
- Add support for IPv6 Virtual DNS ([QubesOS#462](QubesOS#462))
2 parents ad07468 + a8e0e55 commit 4e79c76

4 files changed

Lines changed: 121 additions & 26 deletions

File tree

network/qubes-setup-dnat-to-ns

Lines changed: 66 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,8 @@ import sys
2727
import dbus
2828
import qubesdb
2929
from typing import List
30-
from ipaddress import IPv4Address
30+
from itertools import zip_longest
31+
from ipaddress import ip_address, IPv4Address, IPv6Address
3132
import os
3233

3334
def get_dns_resolv_conf():
@@ -42,7 +43,7 @@ def get_dns_resolv_conf():
4243
if len(tokens) < 2 or tokens[0] != "nameserver":
4344
continue
4445
try:
45-
nameservers.append(IPv4Address(tokens[1]))
46+
nameservers.append(ip_address(tokens[1]))
4647
except ValueError:
4748
pass
4849
return nameservers
@@ -71,16 +72,15 @@ def get_dns_resolved():
7172
raise
7273
# Use global entries first
7374
dns.sort(key=lambda x: x[0] != 0)
74-
# Only keep IPv4 entries. systemd-resolved is trusted to return valid
75-
# addresses.
75+
# systemd-resolved is trusted to return valid addresses.
7676
# ToDo: We only need abridged IPv4 DNS entries for ifindex == 0.
7777
# to ensure static DNS of disconnected network interfaces are not added.
78-
return [IPv4Address(bytes(addr)) for ifindex, family, addr in dns
79-
if family == 2]
78+
return [ip_address(bytes(addr)) for ifindex, family, addr in dns]
8079

8180
def install_firewall_rules(dns):
8281
qdb = qubesdb.QubesDB()
8382
qubesdb_dns = []
83+
qubesdb_dns6 = []
8484
for i in ('/qubes-netvm-primary-dns', '/qubes-netvm-secondary-dns'):
8585
ns_maybe = qdb.read(i)
8686
if ns_maybe is None:
@@ -89,6 +89,14 @@ def install_firewall_rules(dns):
8989
qubesdb_dns.append(IPv4Address(ns_maybe.decode("ascii", "strict")))
9090
except (UnicodeDecodeError, ValueError):
9191
pass
92+
for i in ('/qubes-netvm-primary-dns6', '/qubes-netvm-secondary-dns6'):
93+
ns_maybe = qdb.read(i)
94+
if ns_maybe is None:
95+
continue
96+
try:
97+
qubesdb_dns6.append(IPv6Address(ns_maybe.decode("ascii", "strict")))
98+
except (UnicodeDecodeError, ValueError):
99+
pass
92100
preamble = [
93101
'add table ip qubes',
94102
# Add the chain so that the subsequent delete will work. If the chain already
@@ -102,8 +110,10 @@ def install_firewall_rules(dns):
102110
]
103111
rules = [
104112
'table ip qubes {',
113+
'chain custom-dnat-dns {}',
105114
'chain dnat-dns {',
106115
'type nat hook prerouting priority dstnat; policy accept;',
116+
'jump custom-dnat-dns',
107117
]
108118
dns_resolved = get_dns_resolved()
109119
if not dns_resolved:
@@ -119,12 +129,22 @@ def install_firewall_rules(dns):
119129
while len(qubesdb_dns) > len(dns_resolved):
120130
# Ensure that upstream DNS pool is larger than qubesdb_dns pool
121131
dns_resolved = dns_resolved + dns_resolved
122-
for vm_nameserver, dest in zip(qubesdb_dns, dns_resolved):
132+
for i, (vm_nameserver, dest) in enumerate(zip_longest(qubesdb_dns,
133+
[dns_ip for dns_ip in dns if type(dns_ip) is IPv4Address])):
134+
if i >= len(qubesdb_dns):
135+
break
123136
dns_ = str(dest)
124-
rules += [
125-
f"ip daddr {vm_nameserver} udp dport 53 dnat to {dns_}",
126-
f"ip daddr {vm_nameserver} tcp dport 53 dnat to {dns_}",
127-
]
137+
if dest is None or (vm_nameserver == dest and
138+
qdb.read('/qubes-ip') is None):
139+
rules += [
140+
f"ip daddr {vm_nameserver} tcp dport 53 reject with icmp type host-unreachable",
141+
f"ip daddr {vm_nameserver} udp dport 53 reject with icmp type host-unreachable",
142+
]
143+
else:
144+
rules += [
145+
f"ip daddr {vm_nameserver} udp dport 53 dnat to {dns_}",
146+
f"ip daddr {vm_nameserver} tcp dport 53 dnat to {dns_}",
147+
]
128148
rules += ["}", "}"]
129149

130150
# check if new rules are the same as the old ones - if so, don't reload
@@ -139,7 +159,41 @@ def install_firewall_rules(dns):
139159
if old_rules == rules:
140160
sys.exit(100)
141161

142-
os.execvp("nft", ("nft", "--", "\n".join(preamble + rules)))
162+
# ipv6
163+
res = [
164+
'add table ip6 qubes',
165+
# Add the chain so that the subsequent delete will work. If the chain already
166+
# exists this is a harmless no-op.
167+
'add chain ip6 qubes dnat-dns',
168+
# Delete the chain so that if the chain already exists, it will be removed.
169+
# The removal of the old chain and addition of the new one happen as a single
170+
# atomic operation, so there is no period where neither chain is present or
171+
# where both are present.
172+
'delete chain ip6 qubes dnat-dns',
173+
'table ip6 qubes {',
174+
'chain custom-dnat-dns {}',
175+
'chain dnat-dns {',
176+
'type nat hook prerouting priority dstnat; policy accept;',
177+
'jump custom-dnat-dns',
178+
]
179+
for i, (vm_nameserver, dest) in enumerate(zip_longest(qubesdb_dns6,
180+
[dns_ip for dns_ip in dns if type(dns_ip) is IPv6Address])):
181+
if i >= len(qubesdb_dns6):
182+
break
183+
dns_ = str(dest)
184+
if dest is None or (vm_nameserver == dest and
185+
qdb.read('/qubes-primary-dns6') is None):
186+
res += [
187+
f"ip6 daddr {vm_nameserver} tcp dport 53 reject with icmpv6 type addr-unreachable",
188+
f"ip6 daddr {vm_nameserver} udp dport 53 reject with icmpv6 type addr-unreachable",
189+
]
190+
else:
191+
res += [
192+
f"ip6 daddr {vm_nameserver} udp dport 53 dnat to {dns_}",
193+
f"ip6 daddr {vm_nameserver} tcp dport 53 dnat to {dns_}",
194+
]
195+
res += ["}\n}\n"]
196+
os.execvp("nft", ("nft", "--", "\n".join(preamble + rules + res)))
143197

144198
if __name__ == '__main__':
145199
install_firewall_rules(get_dns_resolved())

network/setup-ip

Lines changed: 38 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,9 @@ configure_network () {
2525
local gateway6="$8"
2626
local primary_dns="$9"
2727
local secondary_dns="${10}"
28-
local custom="${11}"
28+
local primary_dns6="${11}"
29+
local secondary_dns6="${12}"
30+
local custom="${13}"
2931

3032
ip -- address replace "$ip/$netmask" dev "$INTERFACE"
3133
if [[ "$custom" = false ]]; then
@@ -64,16 +66,21 @@ configure_network () {
6466
if [ -h /etc/resolv.conf ]; then
6567
rm -f /etc/resolv.conf
6668
fi
67-
echo > /etc/resolv.conf
69+
echo -n > /etc/resolv.conf
6870
if ! qsvc disable-dns-server ; then
69-
echo "nameserver $primary_dns" > /etc/resolv.conf
71+
if [ -n "$primary_dns6" ]; then
72+
echo "nameserver $primary_dns6" >> /etc/resolv.conf
73+
echo "nameserver $secondary_dns6" >> /etc/resolv.conf
74+
fi
75+
echo "nameserver $primary_dns" >> /etc/resolv.conf
7076
echo "nameserver $secondary_dns" >> /etc/resolv.conf
7177
fi
7278
fi
7379
if [ -x /usr/bin/resolvectl ] && \
7480
systemctl is-enabled -q systemd-resolved.service && \
7581
! qsvc disable-dns-server ; then
76-
resolvectl dns "$INTERFACE" "$primary_dns" "$secondary_dns"
82+
resolvectl dns "$INTERFACE" "$primary_dns6" "$secondary_dns6" \
83+
"$primary_dns" "$secondary_dns"
7784
fi
7885
}
7986

@@ -88,7 +95,9 @@ configure_network_nm () {
8895
local gateway6="$8"
8996
local primary_dns="$9"
9097
local secondary_dns="${10}"
91-
local custom="${11}"
98+
local primary_dns6="${11}"
99+
local secondary_dns6="${12}"
100+
local custom="${13}"
92101

93102
local prefix
94103
local prefix6
@@ -119,6 +128,10 @@ __EOF__
119128
if ! qsvc disable-dns-server ; then
120129
ip4_nm_config="${ip4_nm_config}
121130
dns=${primary_dns};${secondary_dns}"
131+
if [ -n "$primary_dns6" ]; then
132+
ip6_nm_config="${ip6_nm_config}
133+
dns=${primary_dns6};${secondary_dns6}"
134+
fi
122135
fi
123136
if ! qsvc disable-default-route ; then
124137
ip4_nm_config="${ip4_nm_config}
@@ -183,11 +196,22 @@ configure_qubes_ns() {
183196
#netmask=$(qubesdb-read /qubes-netvm-netmask)
184197
primary_dns=$(qubesdb-read /qubes-netvm-primary-dns 2>/dev/null || echo "$gateway")
185198
secondary_dns=$(qubesdb-read /qubes-netvm-secondary-dns)
186-
echo "NS1=$primary_dns" > /var/run/qubes/qubes-ns
187-
echo "NS2=$secondary_dns" >> /var/run/qubes/qubes-ns
188-
ret=0
189-
/usr/lib/qubes/qubes-setup-dnat-to-ns || ret=$?
190-
[ "$ret" -eq 0 ] || [ "$ret" -eq 100 ] || exit "$ret"
199+
primary_dns6=$(qubesdb-read /qubes-netvm-primary-dns6 ||:)
200+
secondary_dns6=$(qubesdb-read /qubes-netvm-secondary-dns6 ||:)
201+
if [ -n "$primary_dns6" ]; then
202+
cat > /var/run/qubes/qubes-ns<< EOF
203+
NS1=$primary_dns6
204+
NS1=$secondary_dns6
205+
NS3=$primary_dns
206+
NS4=$secondary_dns
207+
EOF
208+
else
209+
cat > /var/run/qubes/qubes-ns<< EOF
210+
NS1=$primary_dns
211+
NS1=$secondary_dns
212+
EOF
213+
fi
214+
/usr/lib/qubes/qubes-setup-dnat-to-ns
191215
}
192216

193217
qubes_ip_change_hook() {
@@ -246,6 +270,8 @@ if [ "$ACTION" == "add" ]; then
246270

247271
primary_dns=$(/usr/bin/qubesdb-read /qubes-primary-dns 2>/dev/null) || primary_dns=
248272
secondary_dns=$(/usr/bin/qubesdb-read /qubes-secondary-dns 2>/dev/null) || secondary_dns=
273+
primary_dns6=$(/usr/bin/qubesdb-read /qubes-primary-dns6 2>/dev/null) || primary_dns6=
274+
secondary_dns6=$(/usr/bin/qubesdb-read /qubes-secondary-dns6 2>/dev/null) || secondary_dns6=
249275
/lib/systemd/systemd-sysctl \
250276
"--prefix=/net/ipv4/conf/all" \
251277
"--prefix=/net/ipv4/neigh/all" \
@@ -259,9 +285,9 @@ if [ "$ACTION" == "add" ]; then
259285
if [ -n "$ip4" ]; then
260286
# If NetworkManager is enabled, let it configure the network
261287
if qsvc network-manager && [ -e /usr/bin/nmcli ]; then
262-
configure_network_nm "$MAC" "$INTERFACE" "$ip4" "$ip6" "$netmask" "$netmask6" "$gateway" "$gateway6" "$primary_dns" "$secondary_dns" "$custom"
288+
configure_network_nm "$MAC" "$INTERFACE" "$ip4" "$ip6" "$netmask" "$netmask6" "$gateway" "$gateway6" "$primary_dns" "$secondary_dns" "$primary_dns6" "$secondary_dns6" "$custom"
263289
else
264-
configure_network "$MAC" "$INTERFACE" "$ip4" "$ip6" "$netmask" "$netmask6" "$gateway" "$gateway6" "$primary_dns" "$secondary_dns" "$custom"
290+
configure_network "$MAC" "$INTERFACE" "$ip4" "$ip6" "$netmask" "$netmask6" "$gateway" "$gateway6" "$primary_dns" "$secondary_dns" "$primary_dns6" "$secondary_dns6" "$custom"
265291
fi
266292

267293
network=$(qubesdb-read /qubes-netvm-network 2>/dev/null) || network=

qubes-rpc/post-install.d/10-qubes-core-agent-features.sh

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -119,6 +119,8 @@ advertise_systemd_service() {
119119
done
120120
}
121121

122+
qvm-features-request supported-feature.ipv6dns=1
123+
122124
advertise_systemd_service network-manager NetworkManager.service \
123125
network-manager.service
124126
advertise_systemd_service modem-manager ModemManager.service

vm-systemd/network-proxy-setup.sh

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,9 +19,22 @@ if [ "x$network" != "x" ]; then
1919
#netmask=$(qubesdb-read /qubes-netvm-netmask)
2020
primary_dns=$(qubesdb-read /qubes-netvm-primary-dns 2>/dev/null || echo "$gateway")
2121
secondary_dns=$(qubesdb-read /qubes-netvm-secondary-dns)
22+
primary_dns6=$(qubesdb-read /qubes-netvm-primary-dns6 ||:)
23+
secondary_dns6=$(qubesdb-read /qubes-netvm-secondary-dns6 ||:)
2224
modprobe netbk 2> /dev/null || modprobe xen-netback || "${modprobe_fail_cmd}"
23-
echo "NS1=$primary_dns" > /var/run/qubes/qubes-ns
24-
echo "NS2=$secondary_dns" >> /var/run/qubes/qubes-ns
25+
if [ -n "$primary_dns6" ]; then
26+
cat > /var/run/qubes/qubes-ns<< EOF
27+
NS1=$primary_dns6
28+
NS1=$secondary_dns6
29+
NS3=$primary_dns
30+
NS4=$secondary_dns
31+
EOF
32+
else
33+
cat > /var/run/qubes/qubes-ns<< EOF
34+
NS1=$primary_dns
35+
NS1=$secondary_dns
36+
EOF
37+
fi
2538
/usr/lib/qubes/qubes-setup-dnat-to-ns
2639
echo "1" > /proc/sys/net/ipv4/ip_forward
2740
# enable also IPv6 forwarding, if IPv6 is enabled

0 commit comments

Comments
 (0)