206 lines
7 KiB
Python
206 lines
7 KiB
Python
"""
|
|
vnet.py: NetgraphNet and NetgraphPipeNet classes that implement virtual networks
|
|
using the FreeBSD Netgraph subsystem.
|
|
"""
|
|
|
|
from core import logger
|
|
from core.bsd.netgraph import connectngnodes
|
|
from core.bsd.netgraph import createngnode
|
|
from core.bsd.netgraph import destroyngnode
|
|
from core.bsd.netgraph import ngmessage
|
|
from core.coreobj import PyCoreNet
|
|
|
|
|
|
class NetgraphNet(PyCoreNet):
|
|
ngtype = None
|
|
nghooks = ()
|
|
|
|
def __init__(self, session, objid=None, name=None, start=True, policy=None):
|
|
PyCoreNet.__init__(self, session, objid, name)
|
|
if name is None:
|
|
name = str(self.objid)
|
|
if policy is not None:
|
|
self.policy = policy
|
|
self.name = name
|
|
self.ngname = "n_%s_%s" % (str(self.objid), self.session.session_id)
|
|
self.ngid = None
|
|
self._netif = {}
|
|
self._linked = {}
|
|
self.up = False
|
|
if start:
|
|
self.startup()
|
|
|
|
def startup(self):
|
|
tmp, self.ngid = createngnode(node_type=self.ngtype, hookstr=self.nghooks, name=self.ngname)
|
|
self.up = True
|
|
|
|
def shutdown(self):
|
|
if not self.up:
|
|
return
|
|
self.up = False
|
|
while self._netif:
|
|
k, netif = self._netif.popitem()
|
|
if netif.pipe:
|
|
pipe = netif.pipe
|
|
netif.pipe = None
|
|
pipe.shutdown()
|
|
else:
|
|
netif.shutdown()
|
|
self._netif.clear()
|
|
self._linked.clear()
|
|
del self.session
|
|
destroyngnode(self.ngname)
|
|
|
|
def attach(self, netif):
|
|
"""
|
|
Attach an interface to this netgraph node. Create a pipe between
|
|
the interface and the hub/switch/wlan node.
|
|
(Note that the PtpNet subclass overrides this method.)
|
|
"""
|
|
if self.up:
|
|
pipe = self.session.addobj(cls=NetgraphPipeNet, start=True)
|
|
pipe.attach(netif)
|
|
hook = "link%d" % len(self._netif)
|
|
pipe.attachnet(self, hook)
|
|
PyCoreNet.attach(self, netif)
|
|
|
|
def detach(self, netif):
|
|
PyCoreNet.detach(self, netif)
|
|
|
|
def linked(self, netif1, netif2):
|
|
# check if the network interfaces are attached to this network
|
|
if self._netif[netif1] != netif1:
|
|
raise ValueError("inconsistency for netif %s" % netif1.name)
|
|
if self._netif[netif2] != netif2:
|
|
raise ValueError("inconsistency for netif %s" % netif2.name)
|
|
|
|
try:
|
|
linked = self._linked[netif1][netif2]
|
|
except KeyError:
|
|
linked = False
|
|
self._linked[netif1][netif2] = linked
|
|
|
|
return linked
|
|
|
|
def unlink(self, netif1, netif2):
|
|
if not self.linked(netif1, netif2):
|
|
return
|
|
msg = ["unlink", "{", "node1=0x%s" % netif1.pipe.ngid]
|
|
msg += ["node2=0x%s" % netif2.pipe.ngid, "}"]
|
|
ngmessage(self.ngname, msg)
|
|
self._linked[netif1][netif2] = False
|
|
|
|
def link(self, netif1, netif2):
|
|
if self.linked(netif1, netif2):
|
|
return
|
|
msg = ["link", "{", "node1=0x%s" % netif1.pipe.ngid]
|
|
msg += ["node2=0x%s" % netif2.pipe.ngid, "}"]
|
|
ngmessage(self.ngname, msg)
|
|
self._linked[netif1][netif2] = True
|
|
|
|
def linknet(self, net):
|
|
"""
|
|
Link this bridge with another by creating a veth pair and installing
|
|
each device into each bridge.
|
|
"""
|
|
raise NotImplementedError
|
|
|
|
def linkconfig(self, netif, bw=None, delay=None,
|
|
loss=None, duplicate=None, jitter=None, netif2=None):
|
|
"""
|
|
Set link effects by modifying the pipe connected to an interface.
|
|
"""
|
|
if not netif.pipe:
|
|
logger.warn("linkconfig for %s but interface %s has no pipe", self.name, netif.name)
|
|
return
|
|
return netif.pipe.linkconfig(netif, bw, delay, loss, duplicate, jitter, netif2)
|
|
|
|
|
|
class NetgraphPipeNet(NetgraphNet):
|
|
ngtype = "pipe"
|
|
nghooks = "upper lower"
|
|
|
|
def __init__(self, session, objid=None, name=None, start=True, policy=None):
|
|
NetgraphNet.__init__(self, session, objid, name, start, policy)
|
|
if start:
|
|
# account for Ethernet header
|
|
ngmessage(self.ngname, ["setcfg", "{", "header_offset=14", "}"])
|
|
|
|
def attach(self, netif):
|
|
"""
|
|
Attach an interface to this pipe node.
|
|
The first interface is connected to the "upper" hook, the second
|
|
connected to the "lower" hook.
|
|
"""
|
|
if len(self._netif) > 1:
|
|
raise ValueError("Netgraph pipes support at most 2 network interfaces")
|
|
if self.up:
|
|
hook = self.gethook()
|
|
connectngnodes(self.ngname, netif.localname, hook, netif.hook)
|
|
if netif.pipe:
|
|
raise ValueError("Interface %s already attached to pipe %s" % (netif.name, netif.pipe.name))
|
|
netif.pipe = self
|
|
self._netif[netif] = netif
|
|
self._linked[netif] = {}
|
|
|
|
def attachnet(self, net, hook):
|
|
"""
|
|
Attach another NetgraphNet to this pipe node.
|
|
"""
|
|
localhook = self.gethook()
|
|
connectngnodes(self.ngname, net.ngname, localhook, hook)
|
|
|
|
def gethook(self):
|
|
"""
|
|
Returns the first hook (e.g. "upper") then the second hook
|
|
(e.g. "lower") based on the number of connections.
|
|
"""
|
|
hooks = self.nghooks.split()
|
|
if len(self._netif) == 0:
|
|
return hooks[0]
|
|
else:
|
|
return hooks[1]
|
|
|
|
def linkconfig(self, netif, bw=None, delay=None,
|
|
loss=None, duplicate=None, jitter=None, netif2=None):
|
|
"""
|
|
Set link effects by sending a Netgraph setcfg message to the pipe.
|
|
"""
|
|
netif.setparam("bw", bw)
|
|
netif.setparam("delay", delay)
|
|
netif.setparam("loss", loss)
|
|
netif.setparam("duplicate", duplicate)
|
|
netif.setparam("jitter", jitter)
|
|
if not self.up:
|
|
return
|
|
params = []
|
|
upstream = []
|
|
downstream = []
|
|
if bw is not None:
|
|
if str(bw) == "0":
|
|
bw = "-1"
|
|
params += ["bandwidth=%s" % bw, ]
|
|
if delay is not None:
|
|
if str(delay) == "0":
|
|
delay = "-1"
|
|
params += ["delay=%s" % delay, ]
|
|
if loss is not None:
|
|
if str(loss) == "0":
|
|
loss = "-1"
|
|
upstream += ["BER=%s" % loss, ]
|
|
downstream += ["BER=%s" % loss, ]
|
|
if duplicate is not None:
|
|
if str(duplicate) == "0":
|
|
duplicate = "-1"
|
|
upstream += ["duplicate=%s" % duplicate, ]
|
|
downstream += ["duplicate=%s" % duplicate, ]
|
|
if jitter:
|
|
logger.warn("jitter parameter ignored for link %s", self.name)
|
|
if len(params) > 0 or len(upstream) > 0 or len(downstream) > 0:
|
|
setcfg = ["setcfg", "{", ] + params
|
|
if len(upstream) > 0:
|
|
setcfg += ["upstream={", ] + upstream + ["}", ]
|
|
if len(downstream) > 0:
|
|
setcfg += ["downstream={", ] + downstream + ["}", ]
|
|
setcfg += ["}", ]
|
|
ngmessage(self.ngname, setcfg)
|