core-extra/daemon/core/netns/nodes.py
2013-08-29 14:21:13 +00:00

401 lines
15 KiB
Python

#
# CORE
# Copyright (c)2010-2013 the Boeing Company.
# See the LICENSE file included in this distribution.
#
# authors: Tom Goff <thomas.goff@boeing.com>
# Jeff Ahrenholz <jeffrey.m.ahrenholz@boeing.com>
#
'''
nodes.py: definition of an LxcNode and CoreNode classes, and other node classes
that inherit from the CoreNode, implementing specific node types.
'''
from vnode import *
from vnet import *
from core.misc.ipaddr import *
from core.api import coreapi
from core.coreobj import PyCoreNode
class CtrlNet(LxBrNet):
policy = "ACCEPT"
CTRLIF_IDX_BASE = 99 # base control interface index
def __init__(self, session, objid = "ctrlnet", name = None,
verbose = False, netid = 1, prefix = None,
hostid = None, start = True, assign_address = True,
updown_script = None):
if not prefix:
prefix = "172.16.%d.0/24" % netid
self.prefix = IPv4Prefix(prefix)
self.hostid = hostid
self.assign_address = assign_address
self.updown_script = updown_script
LxBrNet.__init__(self, session, objid = objid, name = name,
verbose = verbose, start = start)
def startup(self):
LxBrNet.startup(self)
if self.hostid:
addr = self.prefix.addr(self.hostid)
else:
addr = self.prefix.maxaddr()
addrlist = ["%s/%s" % (addr, self.prefix.prefixlen)]
if self.assign_address:
self.addrconfig(addrlist = addrlist)
if self.updown_script is not None:
self.info("interface %s updown script '%s startup' called" % \
(self.brname, self.updown_script))
check_call([self.updown_script, self.brname, "startup"])
def shutdown(self):
if self.updown_script is not None:
self.info("interface %s updown script '%s shutdown' called" % \
(self.brname, self.updown_script))
check_call([self.updown_script, self.brname, "shutdown"])
LxBrNet.shutdown(self)
def tolinkmsgs(self, flags):
''' Do not include CtrlNet in link messages describing this session.
'''
return []
class CoreNode(LxcNode):
apitype = coreapi.CORE_NODE_DEF
class PtpNet(LxBrNet):
policy = "ACCEPT"
def attach(self, netif):
if len(self._netif) > 1:
raise ValueError, \
"Point-to-point links support at most 2 network interfaces"
LxBrNet.attach(self, netif)
def tonodemsg(self, flags):
''' Do not generate a Node Message for point-to-point links. They are
built using a link message instead.
'''
pass
def tolinkmsgs(self, flags):
''' Build CORE API TLVs for a point-to-point link. One Link message
describes this network.
'''
tlvdata = ""
if len(self._netif) != 2:
return tlvdata
(if1, if2) = self._netif.items()
if1 = if1[1]
if2 = if2[1]
tlvdata += coreapi.CoreLinkTlv.pack(coreapi.CORE_TLV_LINK_N1NUMBER,
if1.node.objid)
tlvdata += coreapi.CoreLinkTlv.pack(coreapi.CORE_TLV_LINK_N2NUMBER,
if2.node.objid)
delay = if1.getparam('delay')
bw = if1.getparam('bw')
loss = if1.getparam('loss')
duplicate = if1.getparam('duplicate')
jitter = if1.getparam('jitter')
if delay is not None:
tlvdata += coreapi.CoreLinkTlv.pack(coreapi.CORE_TLV_LINK_DELAY,
delay)
if bw is not None:
tlvdata += coreapi.CoreLinkTlv.pack(coreapi.CORE_TLV_LINK_BW, bw)
if loss is not None:
tlvdata += coreapi.CoreLinkTlv.pack(coreapi.CORE_TLV_LINK_PER,
str(loss))
if duplicate is not None:
tlvdata += coreapi.CoreLinkTlv.pack(coreapi.CORE_TLV_LINK_DUP,
str(duplicate))
if jitter is not None:
tlvdata += coreapi.CoreLinkTlv.pack(coreapi.CORE_TLV_LINK_JITTER,
jitter)
tlvdata += coreapi.CoreLinkTlv.pack(coreapi.CORE_TLV_LINK_TYPE,
self.linktype)
tlvdata += coreapi.CoreLinkTlv.pack(coreapi.CORE_TLV_LINK_IF1NUM, \
if1.node.getifindex(if1))
for addr in if1.addrlist:
(ip, sep, mask) = addr.partition('/')
mask = int(mask)
if isIPv4Address(ip):
family = AF_INET
tlvtypeip = coreapi.CORE_TLV_LINK_IF1IP4
tlvtypemask = coreapi.CORE_TLV_LINK_IF1IP4MASK
else:
family = AF_INET6
tlvtypeip = coreapi.CORE_TLV_LINK_IF1IP6
tlvtypemask = coreapi.CORE_TLV_LINK_IF1IP6MASK
ipl = socket.inet_pton(family, ip)
tlvdata += coreapi.CoreLinkTlv.pack(tlvtypeip,
IPAddr(af=family, addr=ipl))
tlvdata += coreapi.CoreLinkTlv.pack(tlvtypemask, mask)
tlvdata += coreapi.CoreLinkTlv.pack(coreapi.CORE_TLV_LINK_IF2NUM, \
if2.node.getifindex(if2))
for addr in if2.addrlist:
(ip, sep, mask) = addr.partition('/')
mask = int(mask)
if isIPv4Address(ip):
family = AF_INET
tlvtypeip = coreapi.CORE_TLV_LINK_IF2IP4
tlvtypemask = coreapi.CORE_TLV_LINK_IF2IP4MASK
else:
family = AF_INET6
tlvtypeip = coreapi.CORE_TLV_LINK_IF2IP6
tlvtypemask = coreapi.CORE_TLV_LINK_IF2IP6MASK
ipl = socket.inet_pton(family, ip)
tlvdata += coreapi.CoreLinkTlv.pack(tlvtypeip,
IPAddr(af=family, addr=ipl))
tlvdata += coreapi.CoreLinkTlv.pack(tlvtypemask, mask)
msg = coreapi.CoreLinkMessage.pack(flags, tlvdata)
return [msg,]
class SwitchNode(LxBrNet):
apitype = coreapi.CORE_NODE_SWITCH
policy = "ACCEPT"
type = "lanswitch"
class HubNode(LxBrNet):
apitype = coreapi.CORE_NODE_HUB
policy = "ACCEPT"
type = "hub"
def __init__(self, session, objid = None, name = None, verbose = False,
start = True):
''' the Hub node forwards packets to all bridge ports by turning off
the MAC address learning
'''
LxBrNet.__init__(self, session, objid, name, verbose, start)
if start:
check_call([BRCTL_BIN, "setageing", self.brname, "0"])
class WlanNode(LxBrNet):
apitype = coreapi.CORE_NODE_WLAN
linktype = coreapi.CORE_LINK_WIRELESS
policy = "DROP"
type = "wlan"
def __init__(self, session, objid = None, name = None, verbose = False,
start = True, policy = None):
LxBrNet.__init__(self, session, objid, name, verbose, start, policy)
# wireless model such as basic range
self.model = None
# mobility model such as scripted
self.mobility = None
def attach(self, netif):
LxBrNet.attach(self, netif)
if self.model:
netif.poshook = self.model._positioncallback
if netif.node is None:
return
(x,y,z) = netif.node.position.get()
# invokes any netif.poshook
netif.setposition(x, y, z)
#self.model.setlinkparams()
def setmodel(self, model, config):
''' Mobility and wireless model.
'''
if (self.verbose):
self.info("adding model %s" % model._name)
if model._type == coreapi.CORE_TLV_REG_WIRELESS:
self.model = model(session=self.session, objid=self.objid,
verbose=self.verbose, values=config)
if self.model._positioncallback:
for netif in self.netifs():
netif.poshook = self.model._positioncallback
if netif.node is not None:
(x,y,z) = netif.node.position.get()
netif.poshook(netif, x, y, z)
self.model.setlinkparams()
elif model._type == coreapi.CORE_TLV_REG_MOBILITY:
self.mobility = model(session=self.session, objid=self.objid,
verbose=self.verbose, values=config)
def tolinkmsgs(self, flags):
msgs = LxBrNet.tolinkmsgs(self, flags)
if self.model:
msgs += self.model.tolinkmsgs(flags)
return msgs
class RJ45Node(PyCoreNode, PyCoreNetIf):
''' RJ45Node is a physical interface on the host linked to the emulated
network.
'''
apitype = coreapi.CORE_NODE_RJ45
def __init__(self, session, objid = None, name = None, mtu = 1500,
verbose = False, start = True):
PyCoreNode.__init__(self, session, objid, name, verbose=verbose,
start=start)
# this initializes net, params, poshook
PyCoreNetIf.__init__(self, node=self, name=name, mtu = mtu)
self.up = False
self.lock = threading.RLock()
self.ifindex = None
# the following are PyCoreNetIf attributes
self.transport_type = "raw"
self.localname = name
self.type = "rj45"
if start:
self.startup()
def startup(self):
''' Set the interface in the up state.
'''
# interface will also be marked up during net.attach()
self.savestate()
try:
check_call([IP_BIN, "link", "set", self.localname, "up"])
except:
self.warn("Failed to run command: %s link set %s up" % \
(IP_BIN, self.localname))
return
self.up = True
def shutdown(self):
''' Bring the interface down. Remove any addresses and queuing
disciplines.
'''
if not self.up:
return
check_call([IP_BIN, "link", "set", self.localname, "down"])
check_call([IP_BIN, "addr", "flush", "dev", self.localname])
mutecall([TC_BIN, "qdisc", "del", "dev", self.localname, "root"])
self.up = False
self.restorestate()
def attachnet(self, net):
PyCoreNetIf.attachnet(self, net)
def detachnet(self):
PyCoreNetIf.detachnet(self)
def newnetif(self, net = None, addrlist = [], hwaddr = None,
ifindex = None, ifname = None):
''' This is called when linking with another node. Since this node
represents an interface, we do not create another object here,
but attach ourselves to the given network.
'''
self.lock.acquire()
try:
if ifindex is None:
ifindex = 0
if self.net is not None:
raise ValueError, \
"RJ45 nodes support at most 1 network interface"
self._netif[ifindex] = self
self.node = self # PyCoreNetIf.node is self
self.ifindex = ifindex
if net is not None:
self.attachnet(net)
for addr in maketuple(addrlist):
self.addaddr(addr)
return ifindex
finally:
self.lock.release()
def delnetif(self, ifindex):
if ifindex is None:
ifindex = 0
if ifindex not in self._netif:
raise ValueError, "ifindex %s does not exist" % ifindex
self._netif.pop(ifindex)
if ifindex == self.ifindex:
self.shutdown()
else:
raise ValueError, "ifindex %s does not exist" % ifindex
def netif(self, ifindex, net=None):
''' This object is considered the network interface, so we only
return self here. This keeps the RJ45Node compatible with
real nodes.
'''
if net is not None and net == self.net:
return self
if ifindex is None:
ifindex = 0
if ifindex == self.ifindex:
return self
return None
def getifindex(self, netif):
if netif != self:
return None
return self.ifindex
def addaddr(self, addr):
if self.up:
check_call([IP_BIN, "addr", "add", str(addr), "dev", self.name])
PyCoreNetIf.addaddr(self, addr)
def deladdr(self, addr):
if self.up:
check_call([IP_BIN, "addr", "del", str(addr), "dev", self.name])
PyCoreNetIf.deladdr(self, addr)
def savestate(self):
''' Save the addresses and other interface state before using the
interface for emulation purposes. TODO: save/restore the PROMISC flag
'''
self.old_up = False
self.old_addrs = []
cmd = [IP_BIN, "addr", "show", "dev", self.localname]
try:
tmp = subprocess.Popen(cmd, stdout = subprocess.PIPE)
except OSError:
self.warn("Failed to run %s command: %s" % (IP_BIN, cmd))
if tmp.wait():
self.warn("Command failed: %s" % cmd)
return
lines = tmp.stdout.read()
tmp.stdout.close()
for l in lines.split('\n'):
items = l.split()
if len(items) < 2:
continue
if items[1] == "%s:" % self.localname:
flags = items[2][1:-1].split(',')
if "UP" in flags:
self.old_up = True
elif items[0] == "inet":
self.old_addrs.append((items[1], items[3]))
elif items[0] == "inet6":
if items[1][:4] == "fe80":
continue
self.old_addrs.append((items[1], None))
def restorestate(self):
''' Restore the addresses and other interface state after using it.
'''
for addr in self.old_addrs:
if addr[1] is None:
check_call([IP_BIN, "addr", "add", addr[0], "dev",
self.localname])
else:
check_call([IP_BIN, "addr", "add", addr[0], "brd", addr[1],
"dev", self.localname])
if self.old_up:
check_call([IP_BIN, "link", "set", self.localname, "up"])
def setposition(self, x=None, y=None, z=None):
''' Use setposition() from both parent classes.
'''
PyCoreObj.setposition(self, x, y, z)
# invoke any poshook
PyCoreNetIf.setposition(self, x, y, z)
class TunnelNode(GreTapBridge):
apitype = coreapi.CORE_NODE_TUNNEL
policy = "ACCEPT"
type = "tunnel"