Skip to content

Commit

Permalink
Add IPv6 related VM properties
Browse files Browse the repository at this point in the history
Add property for IPv6 address ('ip6'). Build default value similarly to
IPv4 - common prefix + QID or Disp ID (for DispVMs).
This all is disabled unless 'ipv6' feature is enabled. It is inherited
from netvm (not template).
Even when enabled, VM may decide to not use it - or simply not support
it.

QubesOS/qubes-issues#718
  • Loading branch information
marmarek committed Dec 1, 2017
1 parent 315d383 commit c67bef4
Show file tree
Hide file tree
Showing 3 changed files with 92 additions and 9 deletions.
3 changes: 3 additions & 0 deletions qubes/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -108,3 +108,6 @@

#: profiles for admin.backup.* calls
backup_profile_dir = '/etc/qubes/backup'

#: site-local prefix for all VMs
qubes_ipv6_prefix = 'fd09:24ef:4179:0000'
18 changes: 18 additions & 0 deletions qubes/tests/vm/mix/net.py
Original file line number Diff line number Diff line change
Expand Up @@ -118,3 +118,21 @@ def test_151_ip_invalid(self):
self.assertPropertyInvalidValue(vm, 'ip', 'a.b.c.d')
self.assertPropertyInvalidValue(vm, 'ip', '1111.2222.3333.4444')
# TODO: implement and add here: 0.0.0.0, 333.333.333.333

def test_160_ip6(self):
vm = self.get_vm()
self.setup_netvms(vm)
self.assertPropertyDefaultValue(vm, 'ip6', None)
vm.netvm.features['ipv6'] = True
self.assertPropertyDefaultValue(vm, 'ip6',
'{}::a89:{:x}'.format(qubes.config.qubes_ipv6_prefix, vm.qid))
vm.ip6 = 'abcd:efff::1'
self.assertEqual(vm.ip6, 'abcd:efff::1')

def test_161_ip6_invalid(self):
vm = self.get_vm()
self.setup_netvms(vm)
vm.netvm.features['ipv6'] = True
self.assertPropertyInvalidValue(vm, 'ip', 'zzzz')
self.assertPropertyInvalidValue(vm, 'ip',
'1:2:3:4:5:6:7:8:0:a:b:c:d:e:f:0')
80 changes: 71 additions & 9 deletions qubes/vm/mix/net.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,13 +24,16 @@

import os
import re
import socket

import libvirt # pylint: disable=import-error
import qubes
import qubes.config
import qubes.events
import qubes.firewall
import qubes.exc

QUBES_IPV6_PREFIX = 'fd09:24ef:4179:0000'

def _setter_mac(self, prop, value):
''' Helper for setting the MAC address '''
Expand Down Expand Up @@ -61,6 +64,27 @@ def _setter_ip(self, prop, value):
raise ValueError('Invalid IP address value')
return value

def _default_ip6(self):
if not self.is_networked():
return None
if not self.features.check_with_netvm('ipv6', False):
return None
if self.netvm is not None:
return self.netvm.get_ip6_for_vm(self) # pylint: disable=no-member

return self.get_ip6_for_vm(self)


def _setter_ip6(self, prop, value):
# pylint: disable=unused-argument
if not isinstance(value, str):
raise ValueError('IPv6 address must be a string')
value = value.lower()
try:
socket.inet_pton(socket.AF_INET6, value)
except socket.error:
raise ValueError('Invalid IPv6 address value')
return value

def _setter_netvm(self, prop, value):
# pylint: disable=unused-argument
Expand Down Expand Up @@ -92,6 +116,11 @@ class NetVMMixin(qubes.events.Emitter):
setter=_setter_ip,
doc='IP address of this domain.')

ip6 = qubes.property('ip6', type=str,
default=_default_ip6,
setter=_setter_ip6,
doc='IPv6 address of this domain.')

# CORE2: swallowed uses_default_netvm
netvm = qubes.VMProperty('netvm', load_stage=4, allow_none=True,
default=(lambda self: self.app.default_netvm),
Expand Down Expand Up @@ -121,6 +150,12 @@ def visible_ip(self):
return self.features.check_with_template('net.fake-ip', None) or \
self.ip

@qubes.stateless_property
def visible_ip6(self):
'''IPv6 address of this domain as seen by the domain.'''
return self.features.check_with_template('net.fake-ip6', None) or \
self.ip6

@qubes.stateless_property
def visible_gateway(self):
'''Default gateway of this domain as seen by the domain.'''
Expand Down Expand Up @@ -151,6 +186,20 @@ def get_ip_for_vm(vm):
# does not happen, because qid < 253, but may happen in the future.
return '10.137.{}.{}'.format((vm.qid >> 8) & 0xff, vm.qid & 0xff)

@staticmethod
def get_ip6_for_vm(vm):
'''Get IPv6 address for (appvm) domain connected to this (netvm) domain.
Default address is constructed with Qubes-specific site-local prefix,
and IPv4 suffix (0xa89 is 10.137.).
'''
import qubes.vm.dispvm # pylint: disable=redefined-outer-name
if isinstance(vm, qubes.vm.dispvm.DispVM):
return '{}::a8a:{:x}'.format(
qubes.config.qubes_ipv6_prefix, vm.dispid)

return '{}::a89:{:x}'.format(qubes.config.qubes_ipv6_prefix, vm.qid)

@qubes.stateless_property
def gateway(self):
'''Gateway for other domains that use this domain as netvm.'''
Expand Down Expand Up @@ -305,15 +354,20 @@ def reload_firewall_for_vm(self, vm):
if not self.is_running():
return

base_dir = '/qubes-firewall/' + vm.ip + '/'
# remove old entries if any (but don't touch base empty entry - it
# would trigger reload right away
self.untrusted_qdb.rm(base_dir)
# write new rules
for key, value in vm.firewall.qdb_entries(addr_family=4).items():
self.untrusted_qdb.write(base_dir + key, value)
# signal its done
self.untrusted_qdb.write(base_dir[:-1], '')
for addr_family in (4, 6):
ip = vm.ip6 if addr_family == 6 else vm.ip
if ip is None:
continue
base_dir = '/qubes-firewall/' + ip + '/'
# remove old entries if any (but don't touch base empty entry - it
# would trigger reload right away
self.untrusted_qdb.rm(base_dir)
# write new rules
for key, value in vm.firewall.qdb_entries(
addr_family=addr_family).items():
self.untrusted_qdb.write(base_dir + key, value)
# signal its done
self.untrusted_qdb.write(base_dir[:-1], '')

def set_mapped_ip_info_for_vm(self, vm):
'''
Expand All @@ -334,6 +388,14 @@ def set_mapped_ip_info_for_vm(self, vm):
else:
self.untrusted_qdb.rm(mapped_ip_base + '/visible-gateway')

if vm.ip6 is not None:
mapped_ip_base = '/mapped-ip/{}'.format(vm.ip6)
if vm.visible_ip6:
self.untrusted_qdb.write(mapped_ip_base + '/visible-ip',
vm.visible_ip6)
else:
self.untrusted_qdb.rm(mapped_ip_base + '/visible-ip')


@qubes.events.handler('property-pre-del:netvm')
def on_property_pre_del_netvm(self, event, name, oldvalue=None):
Expand Down

0 comments on commit c67bef4

Please sign in to comment.