# # CORE # Copyright (c)2010-2012 the Boeing Company. # See the LICENSE file included in this distribution. # # authors: core-dev@pf.itd.nrl.navy.mil # ''' vnet.py: NetgraphNet and NetgraphPipeNet classes that implement virtual networks using the FreeBSD Netgraph subsystem. ''' import sys, threading from core.misc.utils import * from core.constants import * from core.coreobj import PyCoreNet, PyCoreObj from core.bsd.netgraph import * from core.bsd.vnode import VEth class NetgraphNet(PyCoreNet): ngtype = None nghooks = () def __init__(self, session, objid = None, name = None, verbose = False, 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.sessionid) self.ngid = None self.verbose = verbose self._netif = {} self._linked = {} self.up = False if start: self.startup() def startup(self): tmp, self.ngid = createngnode(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, verbose = self.verbose, start = True) pipe.attach(netif) hook = "link%d" % len(self._netif) pipe.attachnet(self, hook) PyCoreNet.attach(self, netif) def detach(self, netif): if self.up: pass 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: self.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, verbose = False, start = True, policy = None): NetgraphNet.__init__(self, session, objid, name, verbose, 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: self.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)