Use uBridge for Qemu connections. Ref #267.

Handle packet captures for VPCS & Qemu nodes. Fixes #548.
This commit is contained in:
grossmj
2016-06-23 16:56:06 -06:00
parent 4eb03b5a99
commit 04022677bd
9 changed files with 244 additions and 119 deletions

View File

@ -75,6 +75,7 @@ class QemuVM(BaseNode):
self._monitor = None
self._stdout_file = ""
self._execute_lock = asyncio.Lock()
self._local_udp_tunnels = {}
# QEMU VM settings
if qemu_path:
@ -882,8 +883,17 @@ class QemuVM(BaseNode):
stdout=fd,
stderr=subprocess.STDOUT,
cwd=self.working_dir)
log.info('QEMU VM "{}" started PID={}'.format(self._name, self._process.pid))
if self.use_ubridge:
yield from self._start_ubridge()
for adapter_number, adapter in enumerate(self._ethernet_adapters):
nio = adapter.get_nio(0)
if nio:
yield from self._add_ubridge_udp_connection("QEMU-{}-{}".format(self._id, adapter_number),
self._local_udp_tunnels[adapter_number][1],
nio)
log.info('QEMU VM "{}" started PID={}'.format(self._name, self._process.pid))
self.status = "started"
monitor_process(self._process, self._termination_callback)
except (OSError, subprocess.SubprocessError, UnicodeEncodeError) as e:
@ -919,6 +929,7 @@ class QemuVM(BaseNode):
Stops this QEMU VM.
"""
yield from self._stop_ubridge()
with (yield from self._execute_lock):
# stop the QEMU process
self._hw_virtualization = False
@ -1003,6 +1014,11 @@ class QemuVM(BaseNode):
if nio and isinstance(nio, NIOUDP):
self.manager.port_manager.release_udp_port(nio.lport, self._project)
for udp_tunnel in self._local_udp_tunnels.values():
self.manager.port_manager.release_udp_port(udp_tunnel[0].lport, self._project)
self.manager.port_manager.release_udp_port(udp_tunnel[1].lport, self._project)
self._local_udp_tunnels = {}
@asyncio.coroutine
def _get_vm_status(self):
"""
@ -1081,27 +1097,12 @@ class QemuVM(BaseNode):
adapter_number=adapter_number))
if self.is_running():
raise QemuError("Sorry, adding a link to a started Qemu VM is not supported.")
# FIXME: does the code below work? very undocumented feature...
# dynamically configure an UDP tunnel on the QEMU VM adapter
# if nio and isinstance(nio, NIOUDP):
# if self._legacy_networking:
# yield from self._control_vm("host_net_remove {} gns3-{}".format(adapter_number, adapter_number))
# yield from self._control_vm("host_net_add udp vlan={},name=gns3-{},sport={},dport={},daddr={}".format(adapter_number,
# adapter_number,
# nio.lport,
# nio.rport,
# nio.rhost))
# else:
# # Apparently there is a bug in Qemu...
# # netdev_add [user|tap|socket|hubport|netmap],id=str[,prop=value][,...] -- add host network device
# # netdev_del id -- remove host network device
# yield from self._control_vm("netdev_del gns3-{}".format(adapter_number))
# yield from self._control_vm("netdev_add socket,id=gns3-{},udp={}:{},localaddr={}:{}".format(adapter_number,
# nio.rhost,
# nio.rport,
# self._host,
# nio.lport))
if self.ubridge:
yield from self._add_ubridge_udp_connection("QEMU-{}-{}".format(self._id, adapter_number),
self._local_udp_tunnels[adapter_number][1],
nio)
else:
raise QemuError("Sorry, adding a link to a started Qemu VM is not supported without using uBridge.")
adapter.add_nio(0, nio)
log.info('QEMU VM "{name}" [{id}]: {nio} added to adapter {adapter_number}'.format(name=self._name,
@ -1126,21 +1127,83 @@ class QemuVM(BaseNode):
adapter_number=adapter_number))
if self.is_running():
# FIXME: does the code below work? very undocumented feature...
# dynamically disable the QEMU VM adapter
yield from self._control_vm("host_net_remove {} gns3-{}".format(adapter_number, adapter_number))
yield from self._control_vm("host_net_add user vlan={},name=gns3-{}".format(adapter_number, adapter_number))
if self.ubridge:
yield from self._ubridge_send("bridge delete {name}".format(name="QEMU-{}-{}".format(self._id, adapter_number)))
else:
raise QemuError("Sorry, removing a link to a started Qemu VM is not supported without using uBridge.")
nio = adapter.get_nio(0)
if isinstance(nio, NIOUDP):
self.manager.port_manager.release_udp_port(nio.lport, self._project)
adapter.remove_nio(0)
log.info('QEMU VM "{name}" [{id}]: {nio} removed from adapter {adapter_number}'.format(name=self._name,
id=self._id,
nio=nio,
adapter_number=adapter_number))
return nio
@asyncio.coroutine
def start_capture(self, adapter_number, output_file):
"""
Starts a packet capture.
:param adapter_number: adapter number
:param output_file: PCAP destination file for the capture
"""
try:
adapter = self._ethernet_adapters[adapter_number]
except IndexError:
raise QemuError('Adapter {adapter_number} does not exist on QEMU VM "{name}"'.format(name=self._name,
adapter_number=adapter_number))
if not self.use_ubridge:
raise QemuError("uBridge must be enabled in order to start packet capture")
nio = adapter.get_nio(0)
if not nio:
raise QemuError("Adapter {} is not connected".format(adapter_number))
if nio.capturing:
raise QemuError("Packet capture is already activated on adapter {adapter_number}".format(adapter_number=adapter_number))
nio.startPacketCapture(output_file)
if self.is_running() and self.ubridge:
yield from self._ubridge_send('bridge start_capture {name} "{output_file}"'.format(name="QEMU-{}-{}".format(self._id, adapter_number),
output_file=output_file))
log.info("QEMU VM '{name}' [{id}]: starting packet capture on adapter {adapter_number}".format(name=self.name,
id=self.id,
adapter_number=adapter_number))
def stop_capture(self, adapter_number):
"""
Stops a packet capture.
:param adapter_number: adapter number
"""
try:
adapter = self._ethernet_adapters[adapter_number]
except IndexError:
raise QemuError('Adapter {adapter_number} does not exist on QEMU VM "{name}"'.format(name=self._name,
adapter_number=adapter_number))
nio = adapter.get_nio(0)
if not nio:
raise QemuError("Adapter {} is not connected".format(adapter_number))
nio.stopPacketCapture()
if self.is_running() and self.ubridge:
yield from self._ubridge_send('bridge stop_capture {name}'.format(name="QEMU-{}-{}".format(self._id, adapter_number)))
log.info("QEMU VM '{name}' [{id}]: stopping packet capture on adapter {adapter_number}".format(name=self.name,
id=self.id,
adapter_number=adapter_number))
@property
def started(self):
"""
@ -1332,7 +1395,14 @@ class QemuVM(BaseNode):
for adapter_number, adapter in enumerate(self._ethernet_adapters):
mac = int_to_macaddress(macaddress_to_int(self._mac_address) + adapter_number)
nio = adapter.get_nio(0)
if self.use_ubridge:
# use a local UDP tunnel to connect to uBridge instead
if adapter_number not in self._local_udp_tunnels:
self._local_udp_tunnels[adapter_number] = self._create_local_udp_tunnel()
nio = self._local_udp_tunnels[adapter_number][0]
else:
nio = adapter.get_nio(0)
if self._legacy_networking:
# legacy QEMU networking syntax (-net)
if nio: