# # 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. """ 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, 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): 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)