From 0e1d8e50719df5af279738f00cc35b67de08cddd Mon Sep 17 00:00:00 2001 From: grossmj Date: Mon, 17 Mar 2014 17:32:16 -0600 Subject: [PATCH] TAP and Ethernet device support for IOU --- gns3server/modules/dynamips/backends/ethsw.py | 34 +++++++------- .../modules/dynamips/dynamips_hypervisor.py | 8 ++-- gns3server/modules/iou/__init__.py | 39 +++++++++++++++- gns3server/modules/iou/iou_device.py | 30 ++++++++---- .../modules/iou/nios/nio_generic_ethernet.py | 46 +++++++++++++++++++ gns3server/modules/iou/nios/nio_tap.py | 46 +++++++++++++++++++ gns3server/modules/iou/nios/nio_udp.py | 3 -- 7 files changed, 172 insertions(+), 34 deletions(-) create mode 100644 gns3server/modules/iou/nios/nio_generic_ethernet.py create mode 100644 gns3server/modules/iou/nios/nio_tap.py diff --git a/gns3server/modules/dynamips/backends/ethsw.py b/gns3server/modules/dynamips/backends/ethsw.py index 891a8a1e..5c288369 100644 --- a/gns3server/modules/dynamips/backends/ethsw.py +++ b/gns3server/modules/dynamips/backends/ethsw.py @@ -99,10 +99,10 @@ class ETHSW(object): Mandatory request parameters: - id (switch identifier) - - ports (ports settings) Optional request parameters: - name (new switch name) + - ports (ports settings) Response parameters: - same as original request @@ -118,22 +118,24 @@ class ETHSW(object): log.debug("received request {}".format(request)) ethsw_id = request["id"] ethsw = self._ethernet_switches[ethsw_id] - ports = request["ports"] - # update the port settings - for port, info in ports.items(): - vlan = info["vlan"] - port_type = info["type"] - try: - if port_type == "access": - ethsw.set_access_port(int(port), vlan) - elif port_type == "dot1q": - ethsw.set_dot1q_port(int(port), vlan) - elif port_type == "qinq": - ethsw.set_qinq_port(int(port), vlan) - except DynamipsError as e: - self.send_custom_error(str(e)) - return + if "ports" in request: + ports = request["ports"] + + # update the port settings + for port, info in ports.items(): + vlan = info["vlan"] + port_type = info["type"] + try: + if port_type == "access": + ethsw.set_access_port(int(port), vlan) + elif port_type == "dot1q": + ethsw.set_dot1q_port(int(port), vlan) + elif port_type == "qinq": + ethsw.set_qinq_port(int(port), vlan) + except DynamipsError as e: + self.send_custom_error(str(e)) + return # rename the switch if requested if "name" in request and ethsw.name != request["name"]: diff --git a/gns3server/modules/dynamips/dynamips_hypervisor.py b/gns3server/modules/dynamips/dynamips_hypervisor.py index c4d554bd..0b746cc6 100644 --- a/gns3server/modules/dynamips/dynamips_hypervisor.py +++ b/gns3server/modules/dynamips/dynamips_hypervisor.py @@ -356,11 +356,11 @@ class DynamipsHypervisor(object): try: if ":" in host: # IPv6 address support - s = socket.socket(socket.AF_INET6, socket_type) + with socket.socket(socket.AF_INET6, socket_type) as s: + s.bind((host, port)) # the port is available if bind is a success else: - s = socket.socket(socket.AF_INET, socket_type) - # the port is available if bind is a success - s.bind((host, port)) + with socket.socket(socket.AF_INET, socket_type) as s: + s.bind((host, port)) # the port is available if bind is a success return port except socket.error as e: if e.errno == errno.EADDRINUSE: # socket already in use diff --git a/gns3server/modules/iou/__init__.py b/gns3server/modules/iou/__init__.py index a51f5df1..06ff909c 100644 --- a/gns3server/modules/iou/__init__.py +++ b/gns3server/modules/iou/__init__.py @@ -23,11 +23,16 @@ import os import sys import base64 import tempfile +import fcntl +import struct +import socket from gns3server.modules import IModule from gns3server.config import Config from .iou_device import IOUDevice from .iou_error import IOUError from .nios.nio_udp import NIO_UDP +from .nios.nio_tap import NIO_TAP +from .nios.nio_generic_ethernet import NIO_GenericEthernet import gns3server.jsonrpc as jsonrpc import logging @@ -469,6 +474,10 @@ class IOU(IModule): - lport (local port) - rhost (remote host) - rport (remote port) + - "NIO_GenericEthernet" + - ethernet_device (Ethernet device name e.g. eth0) + - "NIO_TAP" + - tap_device (TAP device name e.g. tap0) Response parameters: - same as original request @@ -490,12 +499,40 @@ class IOU(IModule): try: nio = None - #TODO: support for TAP and Ethernet NIOs if request["nio"] == "NIO_UDP": lport = request["lport"] rhost = request["rhost"] rport = request["rport"] nio = NIO_UDP(lport, rhost, rport) + elif request["nio"] == "NIO_TAP": + tap_device = request["tap_device"] + + # check that we have access to the tap device + TUNSETIFF = 0x400454ca + IFF_TAP = 0x0002 + IFF_NO_PI = 0x1000 + try: + tun = os.open("/dev/net/tun", os.O_RDWR) + except EnvironmentError as e: + raise IOUError("Could not open /dev/net/tun: {}".format(e)) + ifr = struct.pack("16sH", tap_device.encode("utf-8"), IFF_TAP | IFF_NO_PI) + try: + fcntl.ioctl(tun, TUNSETIFF, ifr) + os.close(tun) + except IOError as e: + raise IOUError("TAP NIO {}: {}".format(tap_device, e)) + + nio = NIO_TAP(tap_device) + elif request["nio"] == "NIO_GenericEthernet": + ethernet_device = request["ethernet_device"] + + # check that we have access to the Ethernet device + try: + with socket.socket(socket.AF_INET, socket.SOCK_RAW, socket.IPPROTO_RAW): + pass + except socket.error as e: + raise IOUError("Generic Ethernet NIO {}: {}".format(ethernet_device, e)) + nio = NIO_GenericEthernet(ethernet_device) if not nio: raise IOUError("Requested NIO doesn't exist or is not supported: {}".format(request["nio"])) except IOUError as e: diff --git a/gns3server/modules/iou/iou_device.py b/gns3server/modules/iou/iou_device.py index 912ee471..9b6ae08c 100644 --- a/gns3server/modules/iou/iou_device.py +++ b/gns3server/modules/iou/iou_device.py @@ -32,7 +32,9 @@ from .ioucon import start_ioucon from .iou_error import IOUError from .adapters.ethernet_adapter import EthernetAdapter from .adapters.serial_adapter import SerialAdapter - +from .nios.nio_udp import NIO_UDP +from .nios.nio_tap import NIO_TAP +from .nios.nio_generic_ethernet import NIO_GenericEthernet import logging log = logging.getLogger(__name__) @@ -323,12 +325,20 @@ class IOUDevice(object): for unit in adapter.ports.keys(): nio = adapter.get_nio(unit) if nio: - #TODO: handle TAP and Ethernet NIOs - tunnel = {"tunnel_udp": "{lport}:{rhost}:{rport}".format(lport=nio.lport, - rhost=nio.rhost, - rport=nio.rport)} + if isinstance(nio, NIO_UDP): + # UDP tunnel + connection = {"tunnel_udp": "{lport}:{rhost}:{rport}".format(lport=nio.lport, + rhost=nio.rhost, + rport=nio.rport)} + elif isinstance(nio, NIO_TAP): + # TAP interface + connection = {"tap_dev": "{tap_device}".format(tap_device=nio.tap_device)} - config["{iouyap_id}:{bay}/{unit}".format(iouyap_id=str(self._id + 512), bay=bay_id, unit=unit_id)] = tunnel + elif isinstance(nio, NIO_GenericEthernet): + # Ethernet interface + connection = {"eth_dev": "{ethernet_device}".format(ethernet_device=nio.ethernet_device)} + + config["{iouyap_id}:{bay}/{unit}".format(iouyap_id=str(self._id + 512), bay=bay_id, unit=unit_id)] = connection unit_id += 1 bay_id += 1 @@ -766,11 +776,11 @@ class IOUDevice(object): try: if ":" in host: # IPv6 address support - s = socket.socket(socket.AF_INET6, socket_type) + with socket.socket(socket.AF_INET6, socket_type) as s: + s.bind((host, port)) # the port is available if bind is a success else: - s = socket.socket(socket.AF_INET, socket_type) - # the port is available if bind is a success - s.bind((host, port)) + with socket.socket(socket.AF_INET, socket_type) as s: + s.bind((host, port)) # the port is available if bind is a success return port except socket.error as e: if e.errno == errno.EADDRINUSE: # socket already in use diff --git a/gns3server/modules/iou/nios/nio_generic_ethernet.py b/gns3server/modules/iou/nios/nio_generic_ethernet.py new file mode 100644 index 00000000..130fb2ff --- /dev/null +++ b/gns3server/modules/iou/nios/nio_generic_ethernet.py @@ -0,0 +1,46 @@ +# -*- coding: utf-8 -*- +# +# Copyright (C) 2013 GNS3 Technologies Inc. +# +# 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 3 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, see . + +""" +Interface for generic Ethernet NIOs (PCAP library). +""" + + +class NIO_GenericEthernet(object): + """ + NIO generic Ethernet NIO. + + :param ethernet_device: Ethernet device name (e.g. eth0) + """ + + def __init__(self, ethernet_device): + + self._ethernet_device = ethernet_device + + @property + def ethernet_device(self): + """ + Returns the Ethernet device used by this NIO. + + :returns: the Ethernet device name + """ + + return self._ethernet_device + + def __str__(self): + + return "NIO Ethernet" diff --git a/gns3server/modules/iou/nios/nio_tap.py b/gns3server/modules/iou/nios/nio_tap.py new file mode 100644 index 00000000..ee550e7b --- /dev/null +++ b/gns3server/modules/iou/nios/nio_tap.py @@ -0,0 +1,46 @@ +# -*- coding: utf-8 -*- +# +# Copyright (C) 2013 GNS3 Technologies Inc. +# +# 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 3 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, see . + +""" +Interface for TAP NIOs (UNIX based OSes only). +""" + + +class NIO_TAP(object): + """ + IOU TAP NIO. + + :param tap_device: TAP device name (e.g. tap0) + """ + + def __init__(self, tap_device): + + self._tap_device = tap_device + + @property + def tap_device(self): + """ + Returns the TAP device used by this NIO. + + :returns: the TAP device name + """ + + return self._tap_device + + def __str__(self): + + return "NIO TAP" diff --git a/gns3server/modules/iou/nios/nio_udp.py b/gns3server/modules/iou/nios/nio_udp.py index bf4353ee..3142d70e 100644 --- a/gns3server/modules/iou/nios/nio_udp.py +++ b/gns3server/modules/iou/nios/nio_udp.py @@ -19,9 +19,6 @@ Interface for UDP NIOs. """ -import logging -log = logging.getLogger(__name__) - class NIO_UDP(object): """