TAP and Ethernet device support for IOU

This commit is contained in:
grossmj 2014-03-17 17:32:16 -06:00
parent 41a1d16e92
commit 0e1d8e5071
7 changed files with 172 additions and 34 deletions

View File

@ -99,10 +99,10 @@ class ETHSW(object):
Mandatory request parameters: Mandatory request parameters:
- id (switch identifier) - id (switch identifier)
- ports (ports settings)
Optional request parameters: Optional request parameters:
- name (new switch name) - name (new switch name)
- ports (ports settings)
Response parameters: Response parameters:
- same as original request - same as original request
@ -118,22 +118,24 @@ class ETHSW(object):
log.debug("received request {}".format(request)) log.debug("received request {}".format(request))
ethsw_id = request["id"] ethsw_id = request["id"]
ethsw = self._ethernet_switches[ethsw_id] ethsw = self._ethernet_switches[ethsw_id]
ports = request["ports"]
# update the port settings if "ports" in request:
for port, info in ports.items(): ports = request["ports"]
vlan = info["vlan"]
port_type = info["type"] # update the port settings
try: for port, info in ports.items():
if port_type == "access": vlan = info["vlan"]
ethsw.set_access_port(int(port), vlan) port_type = info["type"]
elif port_type == "dot1q": try:
ethsw.set_dot1q_port(int(port), vlan) if port_type == "access":
elif port_type == "qinq": ethsw.set_access_port(int(port), vlan)
ethsw.set_qinq_port(int(port), vlan) elif port_type == "dot1q":
except DynamipsError as e: ethsw.set_dot1q_port(int(port), vlan)
self.send_custom_error(str(e)) elif port_type == "qinq":
return ethsw.set_qinq_port(int(port), vlan)
except DynamipsError as e:
self.send_custom_error(str(e))
return
# rename the switch if requested # rename the switch if requested
if "name" in request and ethsw.name != request["name"]: if "name" in request and ethsw.name != request["name"]:

View File

@ -356,11 +356,11 @@ class DynamipsHypervisor(object):
try: try:
if ":" in host: if ":" in host:
# IPv6 address support # 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: else:
s = socket.socket(socket.AF_INET, socket_type) with socket.socket(socket.AF_INET, socket_type) as s:
# the port is available if bind is a success s.bind((host, port)) # the port is available if bind is a success
s.bind((host, port))
return port return port
except socket.error as e: except socket.error as e:
if e.errno == errno.EADDRINUSE: # socket already in use if e.errno == errno.EADDRINUSE: # socket already in use

View File

@ -23,11 +23,16 @@ import os
import sys import sys
import base64 import base64
import tempfile import tempfile
import fcntl
import struct
import socket
from gns3server.modules import IModule from gns3server.modules import IModule
from gns3server.config import Config from gns3server.config import Config
from .iou_device import IOUDevice from .iou_device import IOUDevice
from .iou_error import IOUError from .iou_error import IOUError
from .nios.nio_udp import NIO_UDP 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 gns3server.jsonrpc as jsonrpc
import logging import logging
@ -469,6 +474,10 @@ class IOU(IModule):
- lport (local port) - lport (local port)
- rhost (remote host) - rhost (remote host)
- rport (remote port) - 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: Response parameters:
- same as original request - same as original request
@ -490,12 +499,40 @@ class IOU(IModule):
try: try:
nio = None nio = None
#TODO: support for TAP and Ethernet NIOs
if request["nio"] == "NIO_UDP": if request["nio"] == "NIO_UDP":
lport = request["lport"] lport = request["lport"]
rhost = request["rhost"] rhost = request["rhost"]
rport = request["rport"] rport = request["rport"]
nio = NIO_UDP(lport, rhost, 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: if not nio:
raise IOUError("Requested NIO doesn't exist or is not supported: {}".format(request["nio"])) raise IOUError("Requested NIO doesn't exist or is not supported: {}".format(request["nio"]))
except IOUError as e: except IOUError as e:

View File

@ -32,7 +32,9 @@ from .ioucon import start_ioucon
from .iou_error import IOUError from .iou_error import IOUError
from .adapters.ethernet_adapter import EthernetAdapter from .adapters.ethernet_adapter import EthernetAdapter
from .adapters.serial_adapter import SerialAdapter 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 import logging
log = logging.getLogger(__name__) log = logging.getLogger(__name__)
@ -323,12 +325,20 @@ class IOUDevice(object):
for unit in adapter.ports.keys(): for unit in adapter.ports.keys():
nio = adapter.get_nio(unit) nio = adapter.get_nio(unit)
if nio: if nio:
#TODO: handle TAP and Ethernet NIOs if isinstance(nio, NIO_UDP):
tunnel = {"tunnel_udp": "{lport}:{rhost}:{rport}".format(lport=nio.lport, # UDP tunnel
rhost=nio.rhost, connection = {"tunnel_udp": "{lport}:{rhost}:{rport}".format(lport=nio.lport,
rport=nio.rport)} 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 unit_id += 1
bay_id += 1 bay_id += 1
@ -766,11 +776,11 @@ class IOUDevice(object):
try: try:
if ":" in host: if ":" in host:
# IPv6 address support # 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: else:
s = socket.socket(socket.AF_INET, socket_type) with socket.socket(socket.AF_INET, socket_type) as s:
# the port is available if bind is a success s.bind((host, port)) # the port is available if bind is a success
s.bind((host, port))
return port return port
except socket.error as e: except socket.error as e:
if e.errno == errno.EADDRINUSE: # socket already in use if e.errno == errno.EADDRINUSE: # socket already in use

View File

@ -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 <http://www.gnu.org/licenses/>.
"""
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"

View File

@ -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 <http://www.gnu.org/licenses/>.
"""
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"

View File

@ -19,9 +19,6 @@
Interface for UDP NIOs. Interface for UDP NIOs.
""" """
import logging
log = logging.getLogger(__name__)
class NIO_UDP(object): class NIO_UDP(object):
""" """