2013-03-16 02:39:30 +01:00
|
|
|
#!/usr/bin/python2
|
|
|
|
#
|
|
|
|
# The Qubes OS Project, http://www.qubes-os.org
|
|
|
|
#
|
|
|
|
# Copyright (C) 2010 Joanna Rutkowska <joanna@invisiblethingslab.com>
|
|
|
|
# Copyright (C) 2013 Marek Marczykowski <marmarek@invisiblethingslab.com>
|
|
|
|
#
|
|
|
|
# This program is free software; you can redistribute it and/or
|
|
|
|
# modify it under the terms of the GNU General Public License
|
|
|
|
# as published by the Free Software Foundation; either version 2
|
|
|
|
# of the License, or (at your option) any later version.
|
|
|
|
#
|
|
|
|
# This program is distributed in the hope that it will be useful,
|
|
|
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
|
|
# GNU General Public License for more details.
|
|
|
|
#
|
|
|
|
# You should have received a copy of the GNU General Public License
|
|
|
|
# along with this program; if not, write to the Free Software
|
|
|
|
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
|
|
|
#
|
|
|
|
#
|
2013-03-25 16:23:08 +01:00
|
|
|
import sys
|
2013-03-26 01:28:39 +01:00
|
|
|
import os.path
|
2014-03-13 18:32:13 +01:00
|
|
|
import xen.lowlevel.xs
|
2013-03-16 02:39:30 +01:00
|
|
|
|
|
|
|
from qubes.qubes import QubesVm,register_qubes_vm_class,xs,dry_run
|
|
|
|
from qubes.qubes import defaults,system_path,vm_files
|
|
|
|
from qubes.qubes import QubesVmCollection,QubesException
|
|
|
|
|
|
|
|
class QubesNetVm(QubesVm):
|
|
|
|
"""
|
|
|
|
A class that represents a NetVM. A child of QubesCowVM.
|
|
|
|
"""
|
|
|
|
|
|
|
|
# In which order load this VM type from qubes.xml
|
|
|
|
load_order = 70
|
|
|
|
|
2013-03-16 15:28:18 +01:00
|
|
|
def get_attrs_config(self):
|
|
|
|
attrs_config = super(QubesNetVm, self).get_attrs_config()
|
2014-03-26 04:41:28 +01:00
|
|
|
attrs_config['dir_path']['func'] = \
|
|
|
|
lambda value: value if value is not None else \
|
|
|
|
os.path.join(system_path["qubes_servicevms_dir"], self.name)
|
2013-03-16 02:39:30 +01:00
|
|
|
attrs_config['label']['default'] = defaults["servicevm_label"]
|
|
|
|
attrs_config['memory']['default'] = 200
|
|
|
|
|
|
|
|
# New attributes
|
2014-03-27 17:15:15 +01:00
|
|
|
attrs_config['netid'] = {
|
|
|
|
'save': lambda: str(self.netid),
|
|
|
|
'order': 30,
|
2014-03-26 04:41:28 +01:00
|
|
|
'func': lambda value: value if value is not None else
|
|
|
|
self._collection.get_new_unused_netid() }
|
|
|
|
attrs_config['netprefix'] = {
|
|
|
|
'func': lambda x: "10.137.{0}.".format(self.netid) }
|
|
|
|
attrs_config['dispnetprefix'] = {
|
|
|
|
'func': lambda x: "10.138.{0}.".format(self.netid) }
|
2013-03-16 02:39:30 +01:00
|
|
|
|
|
|
|
# Dont save netvm prop
|
|
|
|
attrs_config['netvm'].pop('save')
|
|
|
|
attrs_config['uses_default_netvm'].pop('save')
|
|
|
|
|
|
|
|
return attrs_config
|
|
|
|
|
|
|
|
def __init__(self, **kwargs):
|
|
|
|
super(QubesNetVm, self).__init__(**kwargs)
|
|
|
|
self.connected_vms = QubesVmCollection()
|
|
|
|
|
|
|
|
self.__network = "10.137.{0}.0".format(self.netid)
|
|
|
|
self.__netmask = defaults["vm_default_netmask"]
|
|
|
|
self.__gateway = self.netprefix + "1"
|
|
|
|
self.__secondary_dns = self.netprefix + "254"
|
|
|
|
|
|
|
|
self.__external_ip_allowed_xids = set()
|
|
|
|
|
|
|
|
@property
|
|
|
|
def type(self):
|
|
|
|
return "NetVM"
|
|
|
|
|
|
|
|
def is_netvm(self):
|
|
|
|
return True
|
|
|
|
|
|
|
|
@property
|
|
|
|
def gateway(self):
|
|
|
|
return self.__gateway
|
|
|
|
|
|
|
|
@property
|
|
|
|
def secondary_dns(self):
|
|
|
|
return self.__secondary_dns
|
|
|
|
|
|
|
|
@property
|
|
|
|
def netmask(self):
|
|
|
|
return self.__netmask
|
|
|
|
|
|
|
|
@property
|
|
|
|
def network(self):
|
|
|
|
return self.__network
|
|
|
|
|
|
|
|
def get_ip_for_vm(self, qid):
|
|
|
|
lo = qid % 253 + 2
|
|
|
|
assert lo >= 2 and lo <= 254, "Wrong IP address for VM"
|
|
|
|
return self.netprefix + "{0}".format(lo)
|
|
|
|
|
|
|
|
def get_ip_for_dispvm(self, dispid):
|
|
|
|
lo = dispid % 254 + 1
|
|
|
|
assert lo >= 1 and lo <= 254, "Wrong IP address for VM"
|
|
|
|
return self.dispnetprefix + "{0}".format(lo)
|
|
|
|
|
|
|
|
def create_xenstore_entries(self, xid = None):
|
|
|
|
if dry_run:
|
|
|
|
return
|
|
|
|
|
|
|
|
if xid is None:
|
|
|
|
xid = self.xid
|
|
|
|
|
|
|
|
|
|
|
|
super(QubesNetVm, self).create_xenstore_entries(xid)
|
|
|
|
xs.write('', "/local/domain/{0}/qubes-netvm-external-ip".format(xid), '')
|
|
|
|
self.update_external_ip_permissions(xid)
|
|
|
|
|
|
|
|
def update_external_ip_permissions(self, xid = -1):
|
|
|
|
if xid < 0:
|
|
|
|
xid = self.get_xid()
|
|
|
|
if xid < 0:
|
|
|
|
return
|
|
|
|
|
2013-03-16 16:13:15 +01:00
|
|
|
perms = [ { 'dom': xid } ]
|
2013-03-16 02:39:30 +01:00
|
|
|
|
2013-03-16 16:13:15 +01:00
|
|
|
for xid in self.__external_ip_allowed_xids:
|
|
|
|
perms.append({ 'dom': xid, 'read': True })
|
2013-03-16 02:39:30 +01:00
|
|
|
|
2014-03-13 18:32:13 +01:00
|
|
|
try:
|
|
|
|
xs.set_permissions('', '/local/domain/{0}/qubes-netvm-external-ip'.format(xid),
|
|
|
|
perms)
|
|
|
|
except xen.lowlevel.xs.Error as e:
|
|
|
|
print >>sys.stderr, "WARNING: failed to update external IP " \
|
|
|
|
"permissions: %s" % (str(e))
|
2013-03-16 02:39:30 +01:00
|
|
|
|
|
|
|
def start(self, **kwargs):
|
|
|
|
if dry_run:
|
|
|
|
return
|
|
|
|
|
|
|
|
xid=super(QubesNetVm, self).start(**kwargs)
|
|
|
|
|
|
|
|
# Connect vif's of already running VMs
|
|
|
|
for vm in self.connected_vms.values():
|
|
|
|
if not vm.is_running():
|
|
|
|
continue
|
|
|
|
|
|
|
|
if 'verbose' in kwargs and kwargs['verbose']:
|
|
|
|
print >> sys.stderr, "--> Attaching network to '{0}'...".format(vm.name)
|
|
|
|
|
|
|
|
# Cleanup stale VIFs
|
|
|
|
vm.cleanup_vifs()
|
|
|
|
|
|
|
|
# force frontend to forget about this device
|
|
|
|
# module actually will be loaded back by udev, as soon as network is attached
|
|
|
|
vm.run("modprobe -r xen-netfront xennet", user="root")
|
|
|
|
|
|
|
|
try:
|
|
|
|
vm.attach_network(wait=False)
|
|
|
|
except QubesException as ex:
|
|
|
|
print >> sys.stderr, ("WARNING: Cannot attach to network to '{0}': {1}".format(vm.name, ex))
|
|
|
|
|
|
|
|
return xid
|
|
|
|
|
|
|
|
def shutdown(self, force=False):
|
|
|
|
if dry_run:
|
|
|
|
return
|
|
|
|
|
|
|
|
connected_vms = [vm for vm in self.connected_vms.values() if vm.is_running()]
|
|
|
|
if connected_vms and not force:
|
|
|
|
raise QubesException("There are other VMs connected to this VM: " + str([vm.name for vm in connected_vms]))
|
|
|
|
|
|
|
|
super(QubesNetVm, self).shutdown(force=force)
|
|
|
|
|
|
|
|
def add_external_ip_permission(self, xid):
|
|
|
|
if int(xid) < 0:
|
|
|
|
return
|
|
|
|
self.__external_ip_allowed_xids.add(int(xid))
|
|
|
|
self.update_external_ip_permissions()
|
|
|
|
|
|
|
|
def remove_external_ip_permission(self, xid):
|
|
|
|
self.__external_ip_allowed_xids.discard(int(xid))
|
|
|
|
self.update_external_ip_permissions()
|
|
|
|
|
|
|
|
register_qubes_vm_class(QubesNetVm)
|