initial import (Boeing r1752, NRL r878)

This commit is contained in:
ahrenholz 2013-08-29 14:21:13 +00:00
commit f8f46d28be
394 changed files with 99738 additions and 0 deletions

View file

@ -0,0 +1,53 @@
#!/bin/bash
# Sample controlnet up/down script that will be executed when the control
# network is brought up or down. This script either adds an interface to the
# controlnet bridge or adds a permissive iptables firewall rule.
controlnet_intf=$1
action=$2
config_type=iptables # iptables or brctl
iptables_address=10.205.15.132
brctl_intf=eth2
BRCTL=/sbin/brctl
IPTABLES=/usr/sbin/iptables
case "$action" in
startup)
case "$config_type" in
iptables)
$IPTABLES -I FORWARD -i $controlnet_intf -d $iptables_address -j ACCEPT
$IPTABLES -I FORWARD -o $controlnet_intf -s $iptables_address -j ACCEPT
;;
brctl)
$BRCTL addif $controlnet_intf $brctl_intf
;;
*)
echo "Invalid config_type $config_type"
;;
esac
;;
shutdown)
case "$config_type" in
iptables)
$IPTABLES -D FORWARD -i $controlnet_intf -d $iptables_address -j ACCEPT
$IPTABLES -D FORWARD -o $controlnet_intf -s $iptables_address -j ACCEPT
;;
brctl)
$BRCTL delif $controlnet_intf $brctl_intf
;;
*)
echo "Invalid config_type $config_type"
;;
esac
;;
*)
echo "Invalid action $action"
exit 1
;;
esac
exit 0

View file

@ -0,0 +1,187 @@
#!/usr/bin/env python
#
# CORE
# Copyright (c) 2013 the Boeing Company.
# See the LICENSE file included in this distribution.
#
# author: Jeff Ahrenholz <jeffrey.m.ahrenholz@boeing.com>
#
'''
emanemodel2core.py: scans an EMANE model source file
(e.g. emane/models/rfpipe/maclayer/rfpipemaclayer.cc) and outputs Python
bindings that allow the model to be used in CORE.
When using this conversion utility, you should replace XYZ, Xyz, and xyz with
the actual model name. Note the capitalization convention.
'''
import os, sys, optparse
MODEL_TEMPLATE_PART1 = """
#
# CORE
# Copyright (c)2013 Company.
# See the LICENSE file included in this distribution.
#
# author: Name <email@company.com>
#
'''
xyz.py: EMANE XYZ model bindings for CORE
'''
from core.api import coreapi
from emane import EmaneModel
from universal import EmaneUniversalModel
class EmaneXyzModel(EmaneModel):
def __init__(self, session, objid = None, verbose = False):
EmaneModel.__init__(self, session, objid, verbose)
# model name
_name = "emane_xyz"
# MAC parameters
_confmatrix_mac = [
"""
MODEL_TEMPLATE_PART2 = """
]
# PHY parameters from Universal PHY
_confmatrix_phy = EmaneUniversalModel._confmatrix
_confmatrix = _confmatrix_mac + _confmatrix_phy
# value groupings
_confgroups = "XYZ MAC Parameters:1-%d|Universal PHY Parameters:%d-%d" \
% ( len(_confmatrix_mac), len(_confmatrix_mac) + 1, len(_confmatrix))
def buildnemxmlfiles(self, e, ifc):
''' Build the necessary nem, mac, and phy XMLs in the given path.
If an individual NEM has a nonstandard config, we need to build
that file also. Otherwise the WLAN-wide nXXemane_xyznem.xml,
nXXemane_xyzmac.xml, nXXemane_xyzphy.xml are used.
'''
values = e.getifcconfig(self.objid, self._name,
self.getdefaultvalues(), ifc)
if values is None:
return
nemdoc = e.xmldoc("nem")
nem = nemdoc.getElementsByTagName("nem").pop()
nem.setAttribute("name", "XYZ NEM")
mactag = nemdoc.createElement("mac")
mactag.setAttribute("definition", self.macxmlname(ifc))
nem.appendChild(mactag)
phytag = nemdoc.createElement("phy")
phytag.setAttribute("definition", self.phyxmlname(ifc))
nem.appendChild(phytag)
e.xmlwrite(nemdoc, self.nemxmlname(ifc))
names = list(self.getnames())
macnames = names[:len(self._confmatrix_mac)]
phynames = names[len(self._confmatrix_mac):]
# make any changes to the mac/phy names here to e.g. exclude them from
# the XML output
macdoc = e.xmldoc("mac")
mac = macdoc.getElementsByTagName("mac").pop()
mac.setAttribute("name", "XYZ MAC")
mac.setAttribute("library", "xyzmaclayer")
# append MAC options to macdoc
map( lambda n: mac.appendChild(e.xmlparam(macdoc, n, \
self.valueof(n, values))), macnames)
e.xmlwrite(macdoc, self.macxmlname(ifc))
phydoc = EmaneUniversalModel.getphydoc(e, self, values, phynames)
e.xmlwrite(phydoc, self.phyxmlname(ifc))
"""
def emane_model_source_to_core(infile, outfile):
do_parse_line = False
output = MODEL_TEMPLATE_PART1
with open(infile, 'r') as f:
for line in f:
# begin marker
if "EMANE::ConfigurationDefinition" in line:
do_parse_line = True
# end marker -- all done
if "{0, 0, 0, 0, 0, 0" in line:
break
if do_parse_line:
outstr = convert_line(line)
if outstr is not None:
output += outstr
continue
output += MODEL_TEMPLATE_PART2
if outfile == sys.stdout:
sys.stdout.write(output)
else:
with open(outfile, 'w') as f:
f.write(output)
def convert_line(line):
line = line.strip()
# skip comments
if line.startswith(('/*', '//')):
return None
items = line.strip('{},').split(',')
if len(items) != 7:
#print "continuning on line=", len(items), items
return None
return convert_items_to_line(items)
def convert_items_to_line(items):
fields = ('required', 'default', 'count', 'name', 'value', 'type',
'description')
getfield = lambda(x): items[fields.index(x)].strip()
output = " ("
output += "%s, " % getfield('name')
value = getfield('value')
if value == '"off"':
type = "coreapi.CONF_DATA_TYPE_BOOL"
value = "0"
defaults = '"On,Off"'
elif value == '"on"':
type = "coreapi.CONF_DATA_TYPE_BOOL"
value = '"1"'
defaults = '"On,Off"'
else:
type = "coreapi.CONF_DATA_TYPE_STRING"
defaults = '""'
output += "%s, %s, %s, " % (type, value, defaults)
output += getfield('description')
output += "),\n"
return output
def main():
usagestr = "usage: %prog [-h] [options] -- <command> ..."
parser = optparse.OptionParser(usage = usagestr)
parser.set_defaults(infile = None, outfile = sys.stdout)
parser.add_option("-i", "--infile", dest = "infile",
help = "file to read (usually '*mac.cc')")
parser.add_option("-o", "--outfile", dest = "outfile",
help = "file to write (stdout is default)")
def usage(msg = None, err = 0):
sys.stdout.write("\n")
if msg:
sys.stdout.write(msg + "\n\n")
parser.print_help()
sys.exit(err)
# parse command line options
(options, args) = parser.parse_args()
if options.infile is None:
usage("please specify input file with the '-i' option", err=1)
emane_model_source_to_core(options.infile, options.outfile)
if __name__ == "__main__":
main()

78
daemon/examples/findcore.py Executable file
View file

@ -0,0 +1,78 @@
#!/usr/bin/env python
#
# Search for installed CORE library files and Python bindings.
#
import os, glob
pythondirs = [
"/usr/lib/python2.7/site-packages",
"/usr/lib/python2.7/dist-packages",
"/usr/lib64/python2.7/site-packages",
"/usr/lib64/python2.7/dist-packages",
"/usr/local/lib/python2.7/site-packages",
"/usr/local/lib/python2.7/dist-packages",
"/usr/local/lib64/python2.7/site-packages",
"/usr/local/lib64/python2.7/dist-packages",
"/usr/lib/python2.6/site-packages",
"/usr/lib/python2.6/dist-packages",
"/usr/lib64/python2.6/site-packages",
"/usr/lib64/python2.6/dist-packages",
"/usr/local/lib/python2.6/site-packages",
"/usr/local/lib/python2.6/dist-packages",
"/usr/local/lib64/python2.6/site-packages",
"/usr/local/lib64/python2.6/dist-packages",
]
tcldirs = [
"/usr/lib/core",
"/usr/local/lib/core",
]
def find_in_file(fn, search, column=None):
''' Find a line starting with 'search' in the file given by the filename
'fn'. Return True if found, False if not found, or the column text if
column is specified.
'''
r = False
if not os.path.exists(fn):
return r
f = open(fn, "r")
for line in f:
if line[:len(search)] != search:
continue
r = True
if column is not None:
r = line.split()[column]
break
f.close()
return r
def main():
versions = []
for d in pythondirs:
fn = "%s/core/constants.py" % d
ver = find_in_file(fn, 'COREDPY_VERSION', 2)
if ver:
ver = ver.strip('"')
versions.append((d, ver))
for e in glob.iglob("%s/core_python*egg-info" % d):
ver = find_in_file(e, 'Version:', 1)
if ver:
versions.append((e, ver))
for e in glob.iglob("%s/netns*egg-info" % d):
ver = find_in_file(e, 'Version:', 1)
if ver:
versions.append((e, ver))
for d in tcldirs:
fn = "%s/version.tcl" % d
ver = find_in_file(fn, 'set CORE_VERSION', 2)
if ver:
versions.append((d, ver))
for (d, ver) in versions:
print "%8s %s" % (ver, d)
if __name__ == "__main__":
main()

View file

@ -0,0 +1,26 @@
This directory contains a sample custom service that you can use as a template
for creating your own services.
Follow these steps to add your own services:
1. Modify the sample service MyService to do what you want. It could generate
config/script files, mount per-node directories, start processes/scripts,
etc. sample.py is a Python file that defines one or more classes to be
imported. You can create multiple Python files that will be imported.
Add any new filenames to the __init__.py file.
2. Put these files in a directory such as /home/username/.core/myservices
Note that the last component of this directory name 'myservices' should not
be named something like 'services' which conflicts with an existing Python
name (the syntax 'from myservices import *' is used).
3. Add a 'custom_services_dir = /home/username/.core/myservices' entry to the
/etc/core/core.conf file.
4. Restart the CORE daemon (core-daemon). Any import errors (Python syntax)
should be displayed in the /var/log/core-daemon.log log file (or on screen).
5. Start using your custom service on your nodes. You can create a new node
type that uses your service, or change the default services for an existing
node type, or change individual nodes.

View file

@ -0,0 +1,7 @@
"""myservices
Custom services that you define can be put in this directory. Everything
listed in __all__ is automatically loaded when you add this directory to the
custom_services_dir = '/full/path/to/here' core.conf file option.
"""
__all__ = ["sample"]

View file

@ -0,0 +1,64 @@
#
# CORE
# Copyright (c)2010-2012 the Boeing Company.
# See the LICENSE file included in this distribution.
#
''' Sample user-defined service.
'''
import os
from core.service import CoreService, addservice
from core.misc.ipaddr import IPv4Prefix, IPv6Prefix
class MyService(CoreService):
''' This is a sample user-defined service.
'''
# a unique name is required, without spaces
_name = "MyService"
# you can create your own group here
_group = "Utility"
# list of other services this service depends on
_depends = ()
# per-node directories
_dirs = ()
# generated files (without a full path this file goes in the node's dir,
# e.g. /tmp/pycore.12345/n1.conf/)
_configs = ('myservice.sh', )
# this controls the starting order vs other enabled services
_startindex = 50
# list of startup commands, also may be generated during startup
_startup = ('sh myservice.sh',)
# list of shutdown commands
_shutdown = ()
@classmethod
def generateconfig(cls, node, filename, services):
''' Return a string that will be written to filename, or sent to the
GUI for user customization.
'''
cfg = "#!/bin/sh\n"
cfg += "# auto-generated by MyService (sample.py)\n"
for ifc in node.netifs():
cfg += 'echo "Node %s has interface %s"\n' % (node.name, ifc.name)
# here we do something interesting
cfg += "\n".join(map(cls.subnetentry, ifc.addrlist))
break
return cfg
@staticmethod
def subnetentry(x):
''' Generate a subnet declaration block given an IPv4 prefix string
for inclusion in the config file.
'''
if x.find(":") >= 0:
# this is an IPv6 address
return ""
else:
net = IPv4Prefix(x)
return 'echo " network %s"' % (net)
# this line is required to add the above class to the list of available services
addservice(MyService)

View file

@ -0,0 +1,74 @@
#!/usr/bin/python
#
# Copyright (c)2011-2012 the Boeing Company.
# See the LICENSE file included in this distribution.
#
# Test 3D range calculation of the BasicRangeModel by adding n nodes to a WLAN
# stacked 100 units above each other (using z-axis).
#
import optparse, sys, os, datetime, time
from core import pycore
from core.misc import ipaddr
from core.misc.utils import mutecall
from core.mobility import BasicRangeModel
from core.netns.vnet import EbtablesQueue
def test(numnodes):
# node list
n = []
prefix = ipaddr.IPv4Prefix("10.83.0.0/16")
session = pycore.Session(persistent = True)
wlanid = numnodes + 1
net = session.addobj(cls = pycore.nodes.WlanNode, name = "wlan%d" % wlanid,
objid = wlanid, verbose = True)
net.setmodel(BasicRangeModel, BasicRangeModel.getdefaultvalues())
for i in xrange(1, numnodes + 1):
tmp = session.addobj(cls = pycore.nodes.LxcNode, name = "n%d" % i,
objid = i)
tmp.newnetif(net, ["%s/%s" % (prefix.addr(i), prefix.prefixlen)])
# set increasing Z coordinates
tmp.setposition(10, 10, 100*i)
n.append(tmp)
n[0].term("bash")
# wait for rate seconds to allow ebtables commands to commit
time.sleep(EbtablesQueue.rate)
#session.shutdown()
def main():
usagestr = "usage: %prog [-h] [options] [args]"
parser = optparse.OptionParser(usage = usagestr)
parser.set_defaults(numnodes = 2)
parser.add_option("-n", "--numnodes", dest = "numnodes", type = int,
help = "number of nodes to test; default = %s" %
parser.defaults["numnodes"])
def usage(msg = None, err = 0):
sys.stdout.write("\n")
if msg:
sys.stdout.write(msg + "\n\n")
parser.print_help()
sys.exit(err)
# parse command line options
(options, args) = parser.parse_args()
if options.numnodes < 2:
usage("invalid number of nodes: %s" % options.numnodes)
for a in args:
sys.stderr.write("ignoring command line argument: '%s'\n" % a)
start = datetime.datetime.now()
test(options.numnodes)
print >> sys.stderr, \
"elapsed time: %s" % (datetime.datetime.now() - start)
if __name__ == "__main__":
main()

View file

@ -0,0 +1,99 @@
#!/usr/bin/python -i
# Copyright (c)2010-2013 the Boeing Company.
# See the LICENSE file included in this distribution.
# Example CORE Python script that attaches N nodes to an EMANE 802.11abg
# network. One of the parameters is changed, the pathloss mode.
import sys, datetime, optparse
from core import pycore
from core.misc import ipaddr
from core.constants import *
from core.emane.ieee80211abg import EmaneIeee80211abgModel
# node list (count from 1)
n = [None]
def add_to_server(session):
''' Add this session to the server's list if this script is executed from
the core-daemon server.
'''
global server
try:
server.addsession(session)
return True
except NameError:
return False
def main():
usagestr = "usage: %prog [-h] [options] [args]"
parser = optparse.OptionParser(usage = usagestr)
parser.set_defaults(numnodes = 5)
parser.add_option("-n", "--numnodes", dest = "numnodes", type = int,
help = "number of nodes")
def usage(msg = None, err = 0):
sys.stdout.write("\n")
if msg:
sys.stdout.write(msg + "\n\n")
parser.print_help()
sys.exit(err)
# parse command line options
(options, args) = parser.parse_args()
if options.numnodes < 1:
usage("invalid number of nodes: %s" % options.numnodes)
for a in args:
sys.stderr.write("ignoring command line argument: '%s'\n" % a)
start = datetime.datetime.now()
# IP subnet
prefix = ipaddr.IPv4Prefix("10.83.0.0/16")
# session with some EMANE initialization
session = pycore.Session(persistent=True)
session.master = True
session.location.setrefgeo(47.57917,-122.13232,2.00000)
session.location.refscale = 150.0
session.cfg['emane_models'] = "RfPipe, Ieee80211abg, Bypass, AtdlOmni"
session.emane.loadmodels()
add_to_server(session)
# EMANE WLAN
print "creating EMANE WLAN wlan1"
wlan = session.addobj(cls = pycore.nodes.EmaneNode, name = "wlan1")
wlan.setposition(x=80,y=50)
names = EmaneIeee80211abgModel.getnames()
values = list(EmaneIeee80211abgModel.getdefaultvalues())
# TODO: change any of the EMANE 802.11 parameter values here
values[ names.index('pathlossmode') ] = 'pathloss'
session.emane.setconfig(wlan.objid, EmaneIeee80211abgModel._name, values)
services_str = "zebra|OSPFv3MDR|vtysh|IPForward"
print "creating %d nodes with addresses from %s" % \
(options.numnodes, prefix)
for i in xrange(1, options.numnodes + 1):
tmp = session.addobj(cls = pycore.nodes.CoreNode, name = "n%d" % i,
objid=i)
tmp.newnetif(wlan, ["%s/%s" % (prefix.addr(i), prefix.prefixlen)])
tmp.cmd([SYSCTL_BIN, "net.ipv4.icmp_echo_ignore_broadcasts=0"])
tmp.setposition(x=150*i,y=150)
session.services.addservicestonode(tmp, "", services_str, verbose=False)
n.append(tmp)
# this starts EMANE, etc.
session.instantiate()
# start a shell on node 1
n[1].term("bash")
print "elapsed time: %s" % (datetime.datetime.now() - start)
if __name__ == "__main__" or __name__ == "__builtin__":
main()

View file

@ -0,0 +1,209 @@
#!/usr/bin/python
# Copyright (c)2010-2012 the Boeing Company.
# See the LICENSE file included in this distribution.
#
# author: Jeff Ahrenholz <jeffrey.m.ahrenholz@boeing.com>
#
'''
howmanynodes.py - This is a CORE script that creates network namespace nodes
having one virtual Ethernet interface connected to a bridge. It continues to
add nodes until an exception occurs. The number of nodes per bridge can be
specified.
'''
import optparse, sys, os, datetime, time, shutil
try:
from core import pycore
except ImportError:
# hack for Fedora autoconf that uses the following pythondir:
if "/usr/lib/python2.6/site-packages" in sys.path:
sys.path.append("/usr/local/lib/python2.6/site-packages")
if "/usr/lib64/python2.6/site-packages" in sys.path:
sys.path.append("/usr/local/lib64/python2.6/site-packages")
if "/usr/lib/python2.7/site-packages" in sys.path:
sys.path.append("/usr/local/lib/python2.7/site-packages")
if "/usr/lib64/python2.7/site-packages" in sys.path:
sys.path.append("/usr/local/lib64/python2.7/site-packages")
from core import pycore
from core.misc import ipaddr
from core.constants import *
GBD = 1024.0 * 1024.0
def linuxversion():
''' Return a string having the Linux kernel version.
'''
f = open('/proc/version', 'r')
v = f.readline().split()
version_str = ' '.join(v[:3])
f.close()
return version_str
MEMKEYS = ('total', 'free', 'buff', 'cached', 'stotal', 'sfree')
def memfree():
''' Returns kilobytes memory [total, free, buff, cached, stotal, sfree].
useful stats are:
free memory = free + buff + cached
swap used = stotal - sfree
'''
f = open('/proc/meminfo', 'r')
lines = f.readlines()
f.close()
kbs = {}
for k in MEMKEYS:
kbs[k] = 0
for l in lines:
if l[:9] == "MemTotal:":
kbs['total'] = int(l.split()[1])
elif l[:8] == "MemFree:":
kbs['free'] = int(l.split()[1])
elif l[:8] == "Buffers:":
kbs['buff'] = int(l.split()[1])
elif l[:8] == "Cached:":
kbs['cache'] = int(l.split()[1])
elif l[:10] == "SwapTotal:":
kbs['stotal'] = int(l.split()[1])
elif l[:9] == "SwapFree:":
kbs['sfree'] = int(l.split()[1])
break
return kbs
# node list (count from 1)
nodelist = [None]
switchlist = []
def main():
usagestr = "usage: %prog [-h] [options] [args]"
parser = optparse.OptionParser(usage = usagestr)
parser.set_defaults(waittime = 0.2, numnodes = 0, bridges = 0, retries = 0,
logfile = None, services = None)
parser.add_option("-w", "--waittime", dest = "waittime", type = float,
help = "number of seconds to wait between node creation" \
" (default = %s)" % parser.defaults["waittime"])
parser.add_option("-n", "--numnodes", dest = "numnodes", type = int,
help = "number of nodes (default = unlimited)")
parser.add_option("-b", "--bridges", dest = "bridges", type = int,
help = "number of nodes per bridge; 0 = one bridge " \
"(def. = %s)" % parser.defaults["bridges"])
parser.add_option("-r", "--retry", dest = "retries", type = int,
help = "number of retries on error (default = %s)" % \
parser.defaults["retries"])
parser.add_option("-l", "--log", dest = "logfile", type = str,
help = "log memory usage to this file (default = %s)" % \
parser.defaults["logfile"])
parser.add_option("-s", "--services", dest = "services", type = str,
help = "pipe-delimited list of services added to each " \
"node (default = %s)\n(Example: 'zebra|OSPFv2|OSPFv3|" \
"vtysh|IPForward')" % parser.defaults["services"])
def usage(msg = None, err = 0):
sys.stdout.write("\n")
if msg:
sys.stdout.write(msg + "\n\n")
parser.print_help()
sys.exit(err)
(options, args) = parser.parse_args()
for a in args:
sys.stderr.write("ignoring command line argument: '%s'\n" % a)
start = datetime.datetime.now()
prefix = ipaddr.IPv4Prefix("10.83.0.0/16")
print "Testing how many network namespace nodes this machine can create."
print " - %s" % linuxversion()
mem = memfree()
print " - %.02f GB total memory (%.02f GB swap)" % \
(mem['total']/GBD, mem['stotal']/GBD)
print " - using IPv4 network prefix %s" % prefix
print " - using wait time of %s" % options.waittime
print " - using %d nodes per bridge" % options.bridges
print " - will retry %d times on failure" % options.retries
print " - adding these services to each node: %s" % options.services
print " "
lfp = None
if options.logfile is not None:
# initialize a csv log file header
lfp = open(options.logfile, "a")
lfp.write("# log from howmanynodes.py %s\n" % time.ctime())
lfp.write("# options = %s\n#\n" % options)
lfp.write("# numnodes,%s\n" % ','.join(MEMKEYS))
lfp.flush()
session = pycore.Session(persistent=True)
switch = session.addobj(cls = pycore.nodes.SwitchNode)
switchlist.append(switch)
print "Added bridge %s (%d)." % (switch.brname, len(switchlist))
i = 0
retry_count = options.retries
while True:
i += 1
# optionally add a bridge (options.bridges nodes per bridge)
try:
if options.bridges > 0 and switch.numnetif() >= options.bridges:
switch = session.addobj(cls = pycore.nodes.SwitchNode)
switchlist.append(switch)
print "\nAdded bridge %s (%d) for node %d." % \
(switch.brname, len(switchlist), i)
except Exception, e:
print "At %d bridges (%d nodes) caught exception:\n%s\n" % \
(len(switchlist), i-1, e)
break
# create a node
try:
n = session.addobj(cls = pycore.nodes.LxcNode, name = "n%d" % i)
n.newnetif(switch, ["%s/%s" % (prefix.addr(i), prefix.prefixlen)])
n.cmd([SYSCTL_BIN, "net.ipv4.icmp_echo_ignore_broadcasts=0"])
if options.services is not None:
session.services.addservicestonode(n, "", options.services,
verbose=False)
n.boot()
nodelist.append(n)
if i % 25 == 0:
print "\n%s nodes created " % i,
mem = memfree()
free = mem['free'] + mem['buff'] + mem['cached']
swap = mem['stotal'] - mem['sfree']
print "(%.02f/%.02f GB free/swap)" % (free/GBD , swap/GBD),
if lfp:
lfp.write("%d," % i)
lfp.write("%s\n" % ','.join(str(mem[x]) for x in MEMKEYS))
lfp.flush()
else:
sys.stdout.write(".")
sys.stdout.flush()
time.sleep(options.waittime)
except Exception, e:
print "At %d nodes caught exception:\n" % i, e
if retry_count > 0:
print "\nWill retry creating node %d." % i
shutil.rmtree(n.nodedir, ignore_errors = True)
retry_count -= 1
i -= 1
time.sleep(options.waittime)
continue
else:
print "Stopping at %d nodes!" % i
break
if i == options.numnodes:
print "Stopping at %d nodes due to numnodes option." % i
break
# node creation was successful at this point
retry_count = options.retries
if lfp:
lfp.flush()
lfp.close()
print "elapsed time: %s" % (datetime.datetime.now() - start)
print "Use the core-cleanup script to remove nodes and bridges."
if __name__ == "__main__":
main()

View file

@ -0,0 +1,109 @@
#!/usr/bin/python
# Copyright (c)2013 the Boeing Company.
# See the LICENSE file included in this distribution.
# This script creates a CORE session, that will connect n nodes together
# in a chain, with static routes between nodes
# number of nodes / number of hops
# 2 0
# 3 1
# 4 2
# n n - 2
#
# Use core-cleanup to clean up after this script as the session is left running.
#
import sys, datetime, optparse
from core import pycore
from core.misc import ipaddr
from core.constants import *
# node list (count from 1)
n = [None]
def add_to_server(session):
''' Add this session to the server's list if this script is executed from
the core-daemon server.
'''
global server
try:
server.addsession(session)
return True
except NameError:
return False
def main():
usagestr = "usage: %prog [-h] [options] [args]"
parser = optparse.OptionParser(usage = usagestr)
parser.set_defaults(numnodes = 5)
parser.add_option("-n", "--numnodes", dest = "numnodes", type = int,
help = "number of nodes")
def usage(msg = None, err = 0):
sys.stdout.write("\n")
if msg:
sys.stdout.write(msg + "\n\n")
parser.print_help()
sys.exit(err)
# parse command line options
(options, args) = parser.parse_args()
if options.numnodes < 1:
usage("invalid number of nodes: %s" % options.numnodes)
if options.numnodes >= 255:
usage("invalid number of nodes: %s" % options.numnodes)
for a in args:
sys.stderr.write("ignoring command line argument: '%s'\n" % a)
start = datetime.datetime.now()
session = pycore.Session(persistent=True)
add_to_server(session)
print "creating %d nodes" % options.numnodes
left = None
prefix = None
for i in xrange(1, options.numnodes + 1):
tmp = session.addobj(cls = pycore.nodes.CoreNode, name = "n%d" % i,
objid=i)
if left:
tmp.newnetif(left, ["%s/%s" % (prefix.addr(2), prefix.prefixlen)])
prefix = ipaddr.IPv4Prefix("10.83.%d.0/24" % i) # limit: i < 255
right = session.addobj(cls = pycore.nodes.PtpNet)
tmp.newnetif(right, ["%s/%s" % (prefix.addr(1), prefix.prefixlen)])
tmp.cmd([SYSCTL_BIN, "net.ipv4.icmp_echo_ignore_broadcasts=0"])
tmp.cmd([SYSCTL_BIN, "net.ipv4.conf.all.forwarding=1"])
tmp.cmd([SYSCTL_BIN, "net.ipv4.conf.default.rp_filter=0"])
tmp.setposition(x=100*i,y=150)
n.append(tmp)
left = right
prefixes = map(lambda(x): ipaddr.IPv4Prefix("10.83.%d.0/24" % x),
xrange(1, options.numnodes + 1))
# set up static routing in the chain
for i in xrange(1, options.numnodes + 1):
for j in xrange(1, options.numnodes + 1):
if j < i - 1:
gw = prefixes[i-2].addr(1)
elif j > i:
if i > len(prefixes) - 1:
continue
gw = prefixes[i-1].addr(2)
else:
continue
net = prefixes[j-1]
n[i].cmd([IP_BIN, "route", "add", str(net), "via", str(gw)])
print "elapsed time: %s" % (datetime.datetime.now() - start)
if __name__ == "__main__" or __name__ == "__builtin__":
main()

View file

@ -0,0 +1,284 @@
#!/bin/sh
#
# iperf-performance.sh
#
# (c)2013 the Boeing Company
# authors: Jeff Ahrenholz <jeffrey.m.ahrenholz@boeing.com>
#
# Utility script to automate several iperf runs.
#
# number of iperf runs per test
NUMRUNS=10
# number of seconds per run (10s is iperf default)
RUNTIME=10
# logging
LOG=/tmp/${0}.log
STAMP=`date +%Y%m%d%H%M%S`
#
# client---(loopback)---server
#
loopbacktest () {
killall iperf 2> /dev/null
echo ">> loopback iperf test"
echo "loopback" > ${LOG}
# start an iperf server in the background
# -s = server
# -y c = CSV output
echo "starting local iperf server"
iperf -s -y c >> ${LOG} &
# run an iperf client NUMRUNS times
i=1
while [ $i -le $NUMRUNS ]; do
echo "run $i/$NUMRUNS:"
iperf -t ${RUNTIME} -c localhost
sleep 0.3
i=$((i+1))
done
sleep 1
echo "stopping local iperf server"
killall -v iperf
}
#
# lxc1( client )---veth-pair---lxc2( server )
#
lxcvethtest () {
SERVERIP=10.0.0.1
CLIENTIP=10.0.0.2
SERVER=/tmp/${0}-server
CLIENT=/tmp/${0}-client
echo ">> lxc veth iperf test"
echo "lxcveth" >> ${LOG}
echo "starting lxc iperf server"
vnoded -l $SERVER.log -p $SERVER.pid -c $SERVER
ip link add name veth0.1 type veth peer name veth0
ip link set veth0 netns `cat $SERVER.pid`
vcmd -c $SERVER -- ifconfig veth0 $SERVERIP/24
vcmd -c $SERVER -- iperf -s -y c >> ${LOG} &
echo "starting lxc iperf client"
vnoded -l $CLIENT.log -p $CLIENT.pid -c $CLIENT
ip link set veth0.1 netns `cat $CLIENT.pid`
vcmd -c $CLIENT -- ifconfig veth0.1 $CLIENTIP/24
i=1
while [ $i -le $NUMRUNS ]; do
echo "run $i/$NUMRUNS:"
vcmd -c $CLIENT -- iperf -t ${RUNTIME} -c ${SERVERIP}
sleep 0.3
i=$((i+1))
done
sleep 1
echo "stopping lxc iperf server"
vcmd -c $SERVER -- killall -v iperf
echo "stopping containers"
kill -9 `cat $SERVER.pid`
kill -9 `cat $CLIENT.pid`
echo "cleaning up"
rm -f ${SERVER}*
rm -f ${CLIENT}*
}
#
# lxc1( client veth:):veth---bridge---veth:(:veth server )lxc2
#
lxcbrtest () {
SERVERIP=10.0.0.1
CLIENTIP=10.0.0.2
SERVER=/tmp/${0}-server
CLIENT=/tmp/${0}-client
BRIDGE="lxcbrtest"
echo ">> lxc bridge iperf test"
echo "lxcbr" >> ${LOG}
echo "building bridge"
brctl addbr $BRIDGE
brctl stp $BRIDGE off # disable spanning tree protocol
brctl setfd $BRIDGE 0 # disable forwarding delay
ip link set $BRIDGE up
echo "starting lxc iperf server"
vnoded -l $SERVER.log -p $SERVER.pid -c $SERVER
ip link add name veth0.1 type veth peer name veth0
ip link set veth0 netns `cat $SERVER.pid`
vcmd -c $SERVER -- ifconfig veth0 $SERVERIP/24
brctl addif $BRIDGE veth0.1
ip link set veth0.1 up
vcmd -c $SERVER -- iperf -s -y c >> ${LOG} &
echo "starting lxc iperf client"
vnoded -l $CLIENT.log -p $CLIENT.pid -c $CLIENT
ip link add name veth1.1 type veth peer name veth1
ip link set veth1 netns `cat $CLIENT.pid`
vcmd -c $CLIENT -- ifconfig veth1 $CLIENTIP/24
brctl addif $BRIDGE veth1.1
ip link set veth1.1 up
i=1
while [ $i -le $NUMRUNS ]; do
echo "run $i/$NUMRUNS:"
vcmd -c $CLIENT -- iperf -t ${RUNTIME} -c ${SERVERIP}
sleep 0.3
i=$((i+1))
done
sleep 1
echo "stopping lxc iperf server"
vcmd -c $SERVER -- killall -v iperf
echo "stopping containers"
kill -9 `cat $SERVER.pid`
kill -9 `cat $CLIENT.pid`
echo "cleaning up"
ip link set $BRIDGE down
brctl delbr $BRIDGE
rm -f ${SERVER}*
rm -f ${CLIENT}*
}
#
# n1---n2---n3--- ... ---nN
# N nodes (N-2 hops) in chain with static routing
#
chaintest () {
NUMNODES=$1
SERVERIP=10.83.$NUMNODES.1
if [ -d /tmp/pycore.* ]; then
echo "/tmp/pycore.* already exists, skipping chaintest $NUMNODES"
return
fi
echo ">> n=$NUMNODES node chain iperf test"
echo "chain$NUMNODES" >> ${LOG}
echo "running external chain CORE script with '-n $NUMNODES'"
python iperf-performance-chain.py -n $NUMNODES
echo "starting lxc iperf server on node $NUMNODES"
vcmd -c /tmp/pycore.*/n$NUMNODES -- iperf -s -y c >> ${LOG} &
echo "starting lxc iperf client"
i=1
while [ $i -le $NUMRUNS ]; do
echo "run $i/$NUMRUNS:"
vcmd -c /tmp/pycore.*/n1 -- iperf -t ${RUNTIME} -c ${SERVERIP}
sleep 0.3
i=$((i+1))
done
sleep 1
echo "stopping lxc iperf server"
vcmd -c /tmp/pycore.*/n$NUMNODES -- killall -v iperf
echo "cleaning up"
core-cleanup
}
if [ "z$1" != "z" ]; then
echo "This script takes no parameters and must be run as root."
exit 1
fi
if [ `id -u` != 0 ]; then
echo "This script must be run as root."
exit 1
fi
#
# N lxc clients >---bridge---veth:(:veth server )
#
clientstest () {
NUMCLIENTS=$1
SERVERIP=10.0.0.1
SERVER=/tmp/${0}-server
BRIDGE="lxcbrtest"
echo ">> n=$NUMCLIENTS clients iperf test"
echo "clients$NUMCLIENTS" >> ${LOG}
echo "building bridge"
brctl addbr $BRIDGE
brctl stp $BRIDGE off # disable spanning tree protocol
brctl setfd $BRIDGE 0 # disable forwarding delay
ip link set $BRIDGE up
echo "starting lxc iperf server"
vnoded -l $SERVER.log -p $SERVER.pid -c $SERVER
ip link add name veth0.1 type veth peer name veth0
ip link set veth0 netns `cat $SERVER.pid`
vcmd -c $SERVER -- ifconfig veth0 $SERVERIP/24
brctl addif $BRIDGE veth0.1
ip link set veth0.1 up
vcmd -c $SERVER -- iperf -s -y c >> ${LOG} &
i=1
CLIENTS=""
while [ $i -le $NUMCLIENTS ]; do
echo "starting lxc iperf client $i/$NUMCLIENTS"
CLIENT=/tmp/${0}-client$i
CLIENTIP=10.0.0.1$i
vnoded -l $CLIENT.log -p $CLIENT.pid -c $CLIENT
ip link add name veth1.$i type veth peer name veth1
ip link set veth1 netns `cat $CLIENT.pid`
vcmd -c $CLIENT -- ifconfig veth1 $CLIENTIP/24
brctl addif $BRIDGE veth1.$i
ip link set veth1.$i up
i=$((i+1))
CLIENTS="$CLIENTS $CLIENT"
done
j=1
while [ $j -le $NUMRUNS ]; do
echo "run $j/$NUMRUNS iperf:"
for CLIENT in $CLIENTS; do
vcmd -c $CLIENT -- iperf -t ${RUNTIME} -c ${SERVERIP} &
done
sleep ${RUNTIME} 1
j=$((j+1))
done
sleep 1
echo "stopping lxc iperf server"
vcmd -c $SERVER -- killall -v iperf
echo "stopping containers"
kill -9 `cat $SERVER.pid`
for CLIENT in $CLIENTS; do
kill -9 `cat $CLIENT.pid`
done
# time needed for processes/containers to shut down
sleep 2
echo "cleaning up"
ip link set $BRIDGE down
brctl delbr $BRIDGE
rm -f ${SERVER}*
rm -f /tmp/${0}-client*
# time needed for bridge clean-up
sleep 1
}
#
# run all tests
#
loopbacktest
lxcvethtest
lxcbrtest
chaintest 5
chaintest 10
clientstest 5
clientstest 10
clientstest 15
mv ${LOG} ${PWD}/${0}-${STAMP}.log
echo "===> results in ${PWD}/${0}-${STAMP}.log"

View file

@ -0,0 +1,572 @@
#!/usr/bin/python
# Copyright (c)2011-2012 the Boeing Company.
# See the LICENSE file included in this distribution.
# create a random topology running OSPFv3 MDR, wait and then check
# that all neighbor states are either full or two-way, and check the routes
# in zebra vs those installed in the kernel.
import os, sys, random, time, optparse, datetime
from string import Template
try:
from core import pycore
except ImportError:
# hack for Fedora autoconf that uses the following pythondir:
if "/usr/lib/python2.6/site-packages" in sys.path:
sys.path.append("/usr/local/lib/python2.6/site-packages")
if "/usr/lib64/python2.6/site-packages" in sys.path:
sys.path.append("/usr/local/lib64/python2.6/site-packages")
if "/usr/lib/python2.7/site-packages" in sys.path:
sys.path.append("/usr/local/lib/python2.7/site-packages")
if "/usr/lib64/python2.7/site-packages" in sys.path:
sys.path.append("/usr/local/lib64/python2.7/site-packages")
from core import pycore
from core.misc import ipaddr
from core.misc.utils import mutecall
from core.constants import QUAGGA_STATE_DIR
# sanity check that zebra is installed
try:
mutecall(["zebra", "-u", "root", "-g", "root", "-v"])
except OSError:
sys.stderr.write("ERROR: running zebra failed\n")
sys.exit(1)
class ManetNode(pycore.nodes.LxcNode):
""" An Lxc namespace node configured for Quagga OSPFv3 MANET MDR
"""
conftemp = Template("""\
interface eth0
ip address $ipaddr
ipv6 ospf6 instance-id 65
ipv6 ospf6 hello-interval 2
ipv6 ospf6 dead-interval 6
ipv6 ospf6 retransmit-interval 5
ipv6 ospf6 network manet-designated-router
ipv6 ospf6 diffhellos
ipv6 ospf6 adjacencyconnectivity biconnected
ipv6 ospf6 lsafullness mincostlsa
!
router ospf6
router-id $routerid
interface eth0 area 0.0.0.0
!
ip forwarding
""")
def __init__(self, core, ipaddr, routerid = None,
objid = None, name = None, nodedir = None):
if routerid is None:
routerid = ipaddr.split("/")[0]
self.ipaddr = ipaddr
self.routerid = routerid
pycore.nodes.LxcNode.__init__(self, core, objid, name, nodedir)
self.privatedir(self.confdir)
self.privatedir(QUAGGA_STATE_DIR)
def qconf(self):
return self.conftemp.substitute(ipaddr = self.ipaddr,
routerid = self.routerid)
def config(self):
filename = os.path.join(self.confdir, "Quagga.conf")
f = self.opennodefile(filename, "w")
f.write(self.qconf())
f.close()
pycore.nodes.LxcNode.config(self)
def bootscript(self):
return """\
#!/bin/sh -e
STATEDIR=%s
waitfile()
{
fname=$1
i=0
until [ -e $fname ]; do
i=$(($i + 1))
if [ $i -eq 10 ]; then
echo "file not found: $fname" >&2
exit 1
fi
sleep 0.1
done
}
mkdir -p $STATEDIR
zebra -d -u root -g root
waitfile $STATEDIR/zebra.vty
ospf6d -d -u root -g root
waitfile $STATEDIR/ospf6d.vty
vtysh -b
""" % QUAGGA_STATE_DIR
class Route(object):
""" Helper class for organzing routing table entries. """
def __init__(self, prefix = None, gw = None, metric = None):
try:
self.prefix = ipaddr.IPv4Prefix(prefix)
except Exception, e:
raise ValueError, "Invalid prefix given to Route object: %s\n%s" % \
(prefix, e)
self.gw = gw
self.metric = metric
def __eq__(self, other):
try:
return self.prefix == other.prefix and self.gw == other.gw and \
self.metric == other.metric
except:
return False
def __str__(self):
return "(%s,%s,%s)" % (self.prefix, self.gw, self.metric)
@staticmethod
def key(r):
if not r.prefix:
return 0
return r.prefix.prefix
class ManetExperiment(object):
""" A class for building an MDR network and checking and logging its state.
"""
def __init__(self, options, start):
""" Initialize with options and start time. """
self.session = None
# node list
self.nodes = []
# WLAN network
self.net = None
self.verbose = options.verbose
# dict from OptionParser
self.options = options
self.start = start
self.logbegin()
def info(self, msg):
''' Utility method for writing output to stdout. '''
print msg
sys.stdout.flush()
self.log(msg)
def warn(self, msg):
''' Utility method for writing output to stderr. '''
print >> sys.stderr, msg
sys.stderr.flush()
self.log(msg)
def logbegin(self):
""" Start logging. """
self.logfp = None
if not self.options.logfile:
return
self.logfp = open(self.options.logfile, "w")
self.log("ospfmanetmdrtest begin: %s\n" % self.start.ctime())
def logend(self):
""" End logging. """
if not self.logfp:
return
end = datetime.datetime.now()
self.log("ospfmanetmdrtest end: %s (%s)\n" % \
(end.ctime(), end - self.start))
self.logfp.flush()
self.logfp.close()
self.logfp = None
def log(self, msg):
""" Write to the log file, if any. """
if not self.logfp:
return
print >> self.logfp, msg
def logdata(self, nbrs, mdrs, lsdbs, krs, zrs):
""" Dump experiment parameters and data to the log file. """
self.log("ospfmantetmdrtest data:")
self.log("----- parameters -----")
self.log("%s" % self.options)
self.log("----- neighbors -----")
for rtrid in sorted(nbrs.keys()):
self.log("%s: %s" % (rtrid, nbrs[rtrid]))
self.log("----- mdr levels -----")
self.log(mdrs)
self.log("----- link state databases -----")
for rtrid in sorted(lsdbs.keys()):
self.log("%s lsdb:" % rtrid)
for line in lsdbs[rtrid].split("\n"):
self.log(line)
self.log("----- kernel routes -----")
for rtrid in sorted(krs.keys()):
msg = rtrid + ": "
for rt in krs[rtrid]:
msg += "%s" % rt
self.log(msg)
self.log("----- zebra routes -----")
for rtrid in sorted(zrs.keys()):
msg = rtrid + ": "
for rt in zrs[rtrid]:
msg += "%s" % rt
self.log(msg)
def topology(self, numnodes, linkprob, verbose = False):
""" Build a topology consisting of the given number of ManetNodes
connected to a WLAN and probabilty of links and set
the session, WLAN, and node list objects.
"""
# IP subnet
prefix = ipaddr.IPv4Prefix("10.14.0.0/16")
self.session = pycore.Session()
# emulated network
self.net = self.session.addobj(cls = pycore.nodes.WlanNode)
for i in xrange(1, numnodes + 1):
addr = "%s/%s" % (prefix.addr(i), 32)
tmp = self.session.addobj(cls = ManetNode, ipaddr = addr, name = "n%d" % i)
tmp.newnetif(self.net, [addr])
self.nodes.append(tmp)
# connect nodes with probability linkprob
for i in xrange(numnodes):
for j in xrange(i + 1, numnodes):
r = random.random()
if r < linkprob:
if self.verbose:
self.info("linking (%d,%d)" % (i, j))
self.net.link(self.nodes[i].netif(0), self.nodes[j].netif(0))
# force one link to avoid partitions (should check if this is needed)
j = i
while j == i:
j = random.randint(0, numnodes - 1)
if self.verbose:
self.info("linking (%d,%d)" % (i, j))
self.net.link(self.nodes[i].netif(0), self.nodes[j].netif(0))
self.nodes[i].boot()
# run the boot.sh script on all nodes to start Quagga
for i in xrange(numnodes):
self.nodes[i].cmd(["./%s" % self.nodes[i].bootsh])
def compareroutes(self, node, kr, zr):
""" Compare two lists of Route objects.
"""
kr.sort(key=Route.key)
zr.sort(key=Route.key)
if kr != zr:
self.warn("kernel and zebra routes differ")
if self.verbose:
msg = "kernel: "
for r in kr:
msg += "%s " % r
msg += "\nzebra: "
for r in zr:
msg += "%s " % r
self.warn(msg)
else:
self.info(" kernel and zebra routes match")
def comparemdrlevels(self, nbrs, mdrs):
""" Check that all routers form a connected dominating set, i.e. all
routers are either MDR, BMDR, or adjacent to one.
"""
msg = "All routers form a CDS"
for n in self.nodes:
if mdrs[n.routerid] != "OTHER":
continue
connected = False
for nbr in nbrs[n.routerid]:
if mdrs[nbr] == "MDR" or mdrs[nbr] == "BMDR":
connected = True
break
if not connected:
msg = "All routers do not form a CDS"
self.warn("XXX %s: not in CDS; neighbors: %s" % \
(n.routerid, nbrs[n.routerid]))
if self.verbose:
self.info(msg)
def comparelsdbs(self, lsdbs):
""" Check LSDBs for consistency.
"""
msg = "LSDBs of all routers are consistent"
prev = self.nodes[0]
for n in self.nodes:
db = lsdbs[n.routerid]
if lsdbs[prev.routerid] != db:
msg = "LSDBs of all routers are not consistent"
self.warn("XXX LSDBs inconsistent for %s and %s" % \
(n.routerid, prev.routerid))
i = 0
for entry in lsdbs[n.routerid].split("\n"):
preventries = lsdbs[prev.routerid].split("\n")
try:
preventry = preventries[i]
except IndexError:
preventry = None
if entry != preventry:
self.warn("%s: %s" % (n.routerid, entry))
self.warn("%s: %s" % (prev.routerid, preventry))
i += 1
prev = n
if self.verbose:
self.info(msg)
def checknodes(self):
""" Check the neighbor state and routing tables of all nodes. """
nbrs = {}
mdrs = {}
lsdbs = {}
krs = {}
zrs = {}
v = self.verbose
for n in self.nodes:
self.info("checking %s" % n.name)
nbrs[n.routerid] = Ospf6NeighState(n, verbose=v).run()
krs[n.routerid] = KernelRoutes(n, verbose=v).run()
zrs[n.routerid] = ZebraRoutes(n, verbose=v).run()
self.compareroutes(n, krs[n.routerid], zrs[n.routerid])
mdrs[n.routerid] = Ospf6MdrLevel(n, verbose=v).run()
lsdbs[n.routerid] = Ospf6Database(n, verbose=v).run()
self.comparemdrlevels(nbrs, mdrs)
self.comparelsdbs(lsdbs)
self.logdata(nbrs, mdrs, lsdbs, krs, zrs)
class Cmd:
""" Helper class for running a command on a node and parsing the result. """
args = ""
def __init__(self, node, verbose=False):
""" Initialize with a CoreNode (LxcNode) """
self.id = None
self.stdin = None
self.out = None
self.node = node
self.verbose = verbose
def info(self, msg):
''' Utility method for writing output to stdout.'''
print msg
sys.stdout.flush()
def warn(self, msg):
''' Utility method for writing output to stderr. '''
print >> sys.stderr, "XXX %s:" % self.node.routerid, msg
sys.stderr.flush()
def run(self):
""" This is the primary method used for running this command. """
self.open()
r = self.parse()
self.cleanup()
return r
def open(self):
""" Exceute call to node.popen(). """
self.id, self.stdin, self.out, self.err = \
self.node.popen((self.args))
def parse(self):
""" This method is overloaded by child classes and should return some
result.
"""
return None
def cleanup(self):
""" Close the Popen channels."""
self.stdin.close()
self.out.close()
self.err.close()
tmp = self.id.wait()
if tmp:
self.warn("nonzero exit status:", tmp)
class VtyshCmd(Cmd):
""" Runs a vtysh command. """
def open(self):
args = ("vtysh", "-c", self.args)
self.id, self.stdin, self.out, self.err = self.node.popen((args))
class Ospf6NeighState(VtyshCmd):
""" Check a node for OSPFv3 neighbors in the full/two-way states. """
args = "show ipv6 ospf6 neighbor"
def parse(self):
self.out.readline() # skip first line
nbrlist = []
for line in self.out:
field = line.split()
nbr = field[0]
state = field[3].split("/")[0]
if not state.lower() in ("full", "twoway"):
self.warn("neighbor %s state: %s" % (nbr, state))
nbrlist.append(nbr)
if len(nbrlist) == 0:
self.warn("no neighbors")
if self.verbose:
self.info(" %s has %d neighbors" % (self.node.routerid, len(nbrlist)))
return nbrlist
class Ospf6MdrLevel(VtyshCmd):
""" Retrieve the OSPFv3 MDR level for a node. """
args = "show ipv6 ospf6 mdrlevel"
def parse(self):
line = self.out.readline()
# TODO: handle multiple interfaces
field = line.split()
mdrlevel = field[4]
if not mdrlevel in ("MDR", "BMDR", "OTHER"):
self.warn("mdrlevel: %s" % mdrlevel)
if self.verbose:
self.info(" %s is %s" % (self.node.routerid, mdrlevel))
return mdrlevel
class Ospf6Database(VtyshCmd):
""" Retrieve the OSPFv3 LSDB summary for a node. """
args = "show ipv6 ospf6 database"
def parse(self):
db = ""
for line in self.out:
field = line.split()
if len(field) < 8:
continue
# filter out Age and Duration columns
filtered = field[:3] + field[4:7]
db += " ".join(filtered) + "\n"
return db
class ZebraRoutes(VtyshCmd):
""" Return a list of Route objects for a node based on its zebra
routing table.
"""
args = "show ip route"
def parse(self):
for i in xrange(0,3):
self.out.readline() # skip first three lines
r = []
prefix = None
for line in self.out:
field = line.split()
# only use OSPFv3 selected FIB routes
if field[0][:2] == "o>":
prefix = field[1]
metric = field[2].split("/")[1][:-1]
if field[0][2:] != "*":
continue
if field[3] == "via":
gw = field[4][:-1]
else:
gw = field[6][:-1]
r.append(Route(prefix, gw, metric))
prefix = None
elif prefix and field[0] == "*":
# already have prefix and metric from previous line
gw = field[2][:-1]
r.append(Route(prefix, gw, metric))
prefix = None
if len(r) == 0:
self.warn("no zebra routes")
if self.verbose:
self.info(" %s has %d zebra routes" % (self.node.routerid, len(r)))
return r
class KernelRoutes(Cmd):
""" Return a list of Route objects for a node based on its kernel
routing table.
"""
args = ("/sbin/ip", "route", "show")
def parse(self):
r = []
prefix = None
for line in self.out:
field = line.split()
if field[0] == "nexthop":
if not prefix:
# this saves only the first nexthop entry if multiple exist
continue
else:
prefix = field[0]
metric = field[-1]
tmp = prefix.split("/")
if len(tmp) < 2:
prefix += "/32"
if field[1] == "proto":
# nexthop entry is on the next line
continue
gw = field[2] # nexthop IP or interface
r.append(Route(prefix, gw, metric))
prefix = None
if len(r) == 0:
self.warn("no kernel routes")
if self.verbose:
self.info(" %s has %d kernel routes" % (self.node.routerid, len(r)))
return r
def main():
usagestr = "usage: %prog [-h] [options] [args]"
parser = optparse.OptionParser(usage = usagestr)
parser.set_defaults(numnodes = 10, linkprob = 0.35, delay = 20, seed = None)
parser.add_option("-n", "--numnodes", dest = "numnodes", type = int,
help = "number of nodes")
parser.add_option("-p", "--linkprob", dest = "linkprob", type = float,
help = "link probabilty")
parser.add_option("-d", "--delay", dest = "delay", type = float,
help = "wait time before checking")
parser.add_option("-s", "--seed", dest = "seed", type = int,
help = "specify integer to use for random seed")
parser.add_option("-v", "--verbose", dest = "verbose",
action = "store_true", help = "be more verbose")
parser.add_option("-l", "--logfile", dest = "logfile", type = str,
help = "log detailed output to the specified file")
def usage(msg = None, err = 0):
sys.stdout.write("\n")
if msg:
sys.stdout.write(msg + "\n\n")
parser.print_help()
sys.exit(err)
# parse command line options
(options, args) = parser.parse_args()
if options.numnodes < 2:
usage("invalid numnodes: %s" % options.numnodes)
if options.linkprob <= 0.0 or options.linkprob > 1.0:
usage("invalid linkprob: %s" % options.linkprob)
if options.delay < 0.0:
usage("invalid delay: %s" % options.delay)
for a in args:
sys.stderr.write("ignoring command line argument: '%s'\n" % a)
if options.seed:
random.seed(options.seed)
me = ManetExperiment(options = options, start=datetime.datetime.now())
me.info("creating topology: numnodes = %s; linkprob = %s" % \
(options.numnodes, options.linkprob))
me.topology(options.numnodes, options.linkprob)
me.info("waiting %s sec" % options.delay)
time.sleep(options.delay)
me.info("checking neighbor state and routes")
me.checknodes()
me.info("done")
me.info("elapsed time: %s" % (datetime.datetime.now() - me.start))
me.logend()
return me
if __name__ == "__main__":
me = main()

78
daemon/examples/netns/switch.py Executable file
View file

@ -0,0 +1,78 @@
#!/usr/bin/python -i
# Copyright (c)2010-2013 the Boeing Company.
# See the LICENSE file included in this distribution.
# connect n nodes to a virtual switch/hub
import sys, datetime, optparse
from core import pycore
from core.misc import ipaddr
from core.constants import *
# node list (count from 1)
n = [None]
def add_to_server(session):
''' Add this session to the server's list if this script is executed from
the core-daemon server.
'''
global server
try:
server.addsession(session)
return True
except NameError:
return False
def main():
usagestr = "usage: %prog [-h] [options] [args]"
parser = optparse.OptionParser(usage = usagestr)
parser.set_defaults(numnodes = 5)
parser.add_option("-n", "--numnodes", dest = "numnodes", type = int,
help = "number of nodes")
def usage(msg = None, err = 0):
sys.stdout.write("\n")
if msg:
sys.stdout.write(msg + "\n\n")
parser.print_help()
sys.exit(err)
# parse command line options
(options, args) = parser.parse_args()
if options.numnodes < 1:
usage("invalid number of nodes: %s" % options.numnodes)
for a in args:
sys.stderr.write("ignoring command line argument: '%s'\n" % a)
start = datetime.datetime.now()
# IP subnet
prefix = ipaddr.IPv4Prefix("10.83.0.0/16")
session = pycore.Session(persistent=True)
add_to_server(session)
# emulated Ethernet switch
switch = session.addobj(cls = pycore.nodes.SwitchNode, name = "switch")
switch.setposition(x=80,y=50)
print "creating %d nodes with addresses from %s" % \
(options.numnodes, prefix)
for i in xrange(1, options.numnodes + 1):
tmp = session.addobj(cls = pycore.nodes.CoreNode, name = "n%d" % i,
objid=i)
tmp.newnetif(switch, ["%s/%s" % (prefix.addr(i), prefix.prefixlen)])
tmp.cmd([SYSCTL_BIN, "net.ipv4.icmp_echo_ignore_broadcasts=0"])
tmp.setposition(x=150*i,y=150)
n.append(tmp)
# start a shell on node 1
n[1].term("bash")
print "elapsed time: %s" % (datetime.datetime.now() - start)
if __name__ == "__main__" or __name__ == "__builtin__":
main()

View file

@ -0,0 +1,97 @@
#!/usr/bin/python
# Copyright (c)2010-2012 the Boeing Company.
# See the LICENSE file included in this distribution.
# run iperf to measure the effective throughput between two nodes when
# n nodes are connected to a virtual hub/switch; run test for testsec
# and repeat for minnodes <= n <= maxnodes with a step size of
# nodestep
import optparse, sys, os, datetime
from core import pycore
from core.misc import ipaddr
from core.misc.utils import mutecall
try:
mutecall(["iperf", "-v"])
except OSError:
sys.stderr.write("ERROR: running iperf failed\n")
sys.exit(1)
def test(numnodes, testsec):
# node list
n = []
# IP subnet
prefix = ipaddr.IPv4Prefix("10.83.0.0/16")
session = pycore.Session()
# emulated network
net = session.addobj(cls = pycore.nodes.SwitchNode)
for i in xrange(1, numnodes + 1):
tmp = session.addobj(cls = pycore.nodes.LxcNode, name = "n%d" % i)
tmp.newnetif(net, ["%s/%s" % (prefix.addr(i), prefix.prefixlen)])
n.append(tmp)
n[0].cmd(["iperf", "-s", "-D"])
n[-1].icmd(["iperf", "-t", str(int(testsec)), "-c", str(prefix.addr(1))])
n[0].cmd(["killall", "-9", "iperf"])
session.shutdown()
def main():
usagestr = "usage: %prog [-h] [options] [args]"
parser = optparse.OptionParser(usage = usagestr)
parser.set_defaults(minnodes = 2)
parser.add_option("-m", "--minnodes", dest = "minnodes", type = int,
help = "min number of nodes to test; default = %s" %
parser.defaults["minnodes"])
parser.set_defaults(maxnodes = 2)
parser.add_option("-n", "--maxnodes", dest = "maxnodes", type = int,
help = "max number of nodes to test; default = %s" %
parser.defaults["maxnodes"])
parser.set_defaults(testsec = 10)
parser.add_option("-t", "--testsec", dest = "testsec", type = int,
help = "test time in seconds; default = %s" %
parser.defaults["testsec"])
parser.set_defaults(nodestep = 1)
parser.add_option("-s", "--nodestep", dest = "nodestep", type = int,
help = "number of nodes step size; default = %s" %
parser.defaults["nodestep"])
def usage(msg = None, err = 0):
sys.stdout.write("\n")
if msg:
sys.stdout.write(msg + "\n\n")
parser.print_help()
sys.exit(err)
# parse command line options
(options, args) = parser.parse_args()
if options.minnodes < 2:
usage("invalid min number of nodes: %s" % options.minnodes)
if options.maxnodes < options.minnodes:
usage("invalid max number of nodes: %s" % options.maxnodes)
if options.testsec < 1:
usage("invalid test time: %s" % options.testsec)
if options.nodestep < 1:
usage("invalid node step: %s" % options.nodestep)
for a in args:
sys.stderr.write("ignoring command line argument: '%s'\n" % a)
start = datetime.datetime.now()
for i in xrange(options.minnodes, options.maxnodes + 1, options.nodestep):
print >> sys.stderr, "%s node test:" % i
test(i, options.testsec)
print >> sys.stderr, ""
print >> sys.stderr, \
"elapsed time: %s" % (datetime.datetime.now() - start)
if __name__ == "__main__":
main()

View file

@ -0,0 +1,33 @@
#!/bin/sh
# Below is a transcript of creating two emulated nodes and connecting them
# together with a wired link. You can run the core-cleanup script to clean
# up after this script.
# create node 1 namespace container
vnoded -c /tmp/n1.ctl -l /tmp/n1.log -p /tmp/n1.pid
# create a virtual Ethernet (veth) pair, installing one end into node 1
ip link add name n1.0.1 type veth peer name n1.0
ip link set n1.0 netns `cat /tmp/n1.pid`
vcmd -c /tmp/n1.ctl -- ip link set n1.0 name eth0
vcmd -c /tmp/n1.ctl -- ifconfig eth0 10.0.0.1/24
# create node 2 namespace container
vnoded -c /tmp/n2.ctl -l /tmp/n2.log -p /tmp/n2.pid
# create a virtual Ethernet (veth) pair, installing one end into node 2
ip link add name n2.0.1 type veth peer name n2.0
ip link set n2.0 netns `cat /tmp/n2.pid`
vcmd -c /tmp/n2.ctl -- ip link set n2.0 name eth0
vcmd -c /tmp/n2.ctl -- ifconfig eth0 10.0.0.2/24
# bridge together nodes 1 and 2 using the other end of each veth pair
brctl addbr b.1.1
brctl setfd b.1.1 0
brctl addif b.1.1 n1.0.1
brctl addif b.1.1 n2.0.1
ip link set n1.0.1 up
ip link set n2.0.1 up
ip link set b.1.1 up
# display connectivity and ping from node 1 to node 2
brctl show
vcmd -c /tmp/n1.ctl -- ping 10.0.0.2

View file

@ -0,0 +1,772 @@
#!/usr/bin/python
# Copyright (c)2011-2012 the Boeing Company.
# See the LICENSE file included in this distribution.
#
# author: Jeff Ahrenholz <jeffrey.m.ahrenholz@boeing.com>
#
'''
wlanemanetests.py - This script tests the performance of the WLAN device in
CORE by measuring various metrics:
- delay experienced when pinging end-to-end
- maximum TCP throughput achieved using iperf end-to-end
- the CPU used and loss experienced when running an MGEN flow of UDP traffic
All MANET nodes are arranged in a row, so that any given node can only
communicate with the node to its right or to its left. Performance is measured
using traffic that travels across each hop in the network. Static /32 routing
is used instead of any dynamic routing protocol.
Various underlying network types are tested:
- bridged (the CORE default, uses ebtables)
- bridged with netem (add link effects to the bridge using tc queues)
- EMANE bypass - the bypass model just forwards traffic
- EMANE RF-PIPE - the bandwidth (bitrate) is set very high / no restrictions
- EMANE RF-PIPE - bandwidth is set similar to netem case
- EMANE RF-PIPE - default connectivity is off and pathloss events are
generated to connect the nodes in a line
Results are printed/logged in CSV format.
'''
import os, sys, time, optparse, datetime, math
from string import Template
try:
from core import pycore
except ImportError:
# hack for Fedora autoconf that uses the following pythondir:
if "/usr/lib/python2.6/site-packages" in sys.path:
sys.path.append("/usr/local/lib/python2.6/site-packages")
if "/usr/lib64/python2.6/site-packages" in sys.path:
sys.path.append("/usr/local/lib64/python2.6/site-packages")
if "/usr/lib/python2.7/site-packages" in sys.path:
sys.path.append("/usr/local/lib/python2.7/site-packages")
if "/usr/lib64/python2.7/site-packages" in sys.path:
sys.path.append("/usr/local/lib64/python2.7/site-packages")
from core import pycore
from core.misc import ipaddr
from core.misc.utils import mutecall
from core.constants import QUAGGA_STATE_DIR
from core.emane.bypass import EmaneBypassModel
from core.emane.rfpipe import EmaneRfPipeModel
import emaneeventservice
import emaneeventpathloss
# move these to core.misc.utils
def readstat():
f = open("/proc/stat", "r")
lines = f.readlines()
f.close()
return lines
def numcpus():
lines = readstat()
n = 0
for l in lines[1:]:
if l[:3] != "cpu":
break
n += 1
return n
def getcputimes(line):
# return (user, nice, sys, idle) from a /proc/stat cpu line
# assume columns are:
# cpu# user nice sys idle iowait irq softirq steal guest (man 5 proc)
items = line.split()
(user, nice, sys, idle) = map(lambda(x): int(x), items[1:5])
return [user, nice, sys, idle]
def calculatecpu(timesa, timesb):
for i in range(len(timesa)):
timesb[i] -= timesa[i]
total = sum(timesb)
if total == 0:
return 0.0
else:
# subtract % time spent in idle time
return 100 - ((100.0 * timesb[-1]) / total)
# end move these to core.misc.utils
class Cmd(object):
''' Helper class for running a command on a node and parsing the result. '''
args = ""
def __init__(self, node, verbose=False):
''' Initialize with a CoreNode (LxcNode) '''
self.id = None
self.stdin = None
self.out = None
self.node = node
self.verbose = verbose
def info(self, msg):
''' Utility method for writing output to stdout.'''
print msg
sys.stdout.flush()
def warn(self, msg):
''' Utility method for writing output to stderr. '''
print >> sys.stderr, "XXX %s:" % self.node.name, msg
sys.stderr.flush()
def run(self):
''' This is the primary method used for running this command. '''
self.open()
status = self.id.wait()
r = self.parse()
self.cleanup()
return r
def open(self):
''' Exceute call to node.popen(). '''
self.id, self.stdin, self.out, self.err = \
self.node.popen((self.args))
def parse(self):
''' This method is overloaded by child classes and should return some
result.
'''
return None
def cleanup(self):
''' Close the Popen channels.'''
self.stdin.close()
self.out.close()
self.err.close()
tmp = self.id.wait()
if tmp:
self.warn("nonzero exit status:", tmp)
class ClientServerCmd(Cmd):
''' Helper class for running a command on a node and parsing the result. '''
args = ""
client_args = ""
def __init__(self, node, client_node, verbose=False):
''' Initialize with two CoreNodes, node is the server '''
Cmd.__init__(self, node, verbose)
self.client_node = client_node
def run(self):
''' Run the server command, then the client command, then
kill the server '''
self.open() # server
self.client_open() # client
status = self.client_id.wait()
self.node.cmdresult(['killall', self.args[0]]) # stop the server
r = self.parse()
self.cleanup()
return r
def client_open(self):
''' Exceute call to client_node.popen(). '''
self.client_id, self.client_stdin, self.client_out, self.client_err = \
self.client_node.popen((self.client_args))
def parse(self):
''' This method is overloaded by child classes and should return some
result.
'''
return None
def cleanup(self):
''' Close the Popen channels.'''
self.stdin.close()
self.out.close()
self.err.close()
tmp = self.id.wait()
if tmp:
self.warn("nonzero exit status: %s" % tmp)
self.warn("command was: %s" % ((self.args, )))
class PingCmd(Cmd):
''' Test latency using ping.
'''
def __init__(self, node, verbose=False, addr=None, count=50, interval=0.1, ):
Cmd.__init__(self, node, verbose)
self.addr = addr
self.count = count
self.interval = interval
self.args = ['ping', '-q', '-c', '%s' % count, '-i', '%s' % interval,
addr]
def run(self):
if self.verbose:
self.info("%s initial test ping (max 1 second)..." % self.node.name)
(status, result) = self.node.cmdresult(["ping", "-q", "-c", "1", "-w",
"1", self.addr])
if status != 0:
self.warn("initial ping from %s to %s failed! result:\n%s" % \
(self.node.name, self.addr, result))
return (0.0, 0.0)
if self.verbose:
self.info("%s pinging %s (%d seconds)..." % \
(self.node.name, self.addr, self.count * self.interval))
return Cmd.run(self)
def parse(self):
lines = self.out.readlines()
avg_latency = 0
mdev = 0
try:
stats_str = lines[-1].split('=')[1]
stats = stats_str.split('/')
avg_latency = float(stats[1])
mdev = float(stats[3].split(' ')[0])
except Exception, e:
self.warn("ping parsing exception: %s" % e)
return (avg_latency, mdev)
class IperfCmd(ClientServerCmd):
''' Test throughput using iperf.
'''
def __init__(self, node, client_node, verbose=False, addr=None, time=10):
# node is the server
ClientServerCmd.__init__(self, node, client_node, verbose)
self.addr = addr
self.time = time
# -s server, -y c CSV report output
self.args = ["iperf", "-s", "-y", "c"]
self.client_args = ["iperf", "-c", self.addr, "-t", "%s" % self.time]
def run(self):
if self.verbose:
self.info("Launching the iperf server on %s..." % self.node.name)
self.info("Running the iperf client on %s (%s seconds)..." % \
(self.client_node.name, self.time))
return ClientServerCmd.run(self)
def parse(self):
lines = self.out.readlines()
try:
bps = int(lines[-1].split(',')[-1].strip('\n'))
except Exception, e:
self.warn("iperf parsing exception: %s" % e)
bps = 0
return bps
class MgenCmd(ClientServerCmd):
''' Run a test traffic flow using an MGEN sender and receiver.
'''
def __init__(self, node, client_node, verbose=False, addr=None, time=10,
rate=512):
ClientServerCmd.__init__(self, node, client_node, verbose)
self.addr = addr
self.time = time
self.args = ['mgen', 'event', 'listen udp 5000', 'output',
'/var/log/mgen.log']
self.rate = rate
sendevent = "ON 1 UDP DST %s/5000 PERIODIC [%s]" % \
(addr, self.mgenrate(self.rate))
stopevent = "%s OFF 1" % time
self.client_args = ['mgen', 'event', sendevent, 'event', stopevent,
'output', '/var/log/mgen.log']
@staticmethod
def mgenrate(kbps):
''' Return a MGEN periodic rate string for the given kilobits-per-sec.
Assume 1500 byte MTU, 20-byte IP + 8-byte UDP headers, leaving
1472 bytes for data.
'''
bps = (kbps / 8) * 1000.0
maxdata = 1472
pps = math.ceil(bps / maxdata)
return "%s %s" % (pps, maxdata)
def run(self):
if self.verbose:
self.info("Launching the MGEN receiver on %s..." % self.node.name)
self.info("Running the MGEN sender on %s (%s seconds)..." % \
(self.client_node.name, self.time))
return ClientServerCmd.run(self)
def cleanup(self):
''' Close the Popen channels.'''
self.stdin.close()
self.out.close()
self.err.close()
tmp = self.id.wait() # non-zero mgen exit status OK
def parse(self):
''' Check MGEN receiver's log file for packet sequence numbers, and
return the percentage of lost packets.
'''
logfile = os.path.join(self.node.nodedir, 'var.log/mgen.log')
f = open(logfile, 'r')
numlost = 0
lastseq = 0
for line in f.readlines():
fields = line.split()
if fields[1] != 'RECV':
continue
try:
seq = int(fields[4].split('>')[1])
except:
self.info("Unexpected MGEN line:\n%s" % fields)
if seq > (lastseq + 1):
numlost += seq - (lastseq + 1)
lastseq = seq
f.close()
if lastseq > 0:
loss = 100.0 * numlost / lastseq
else:
loss = 0
if self.verbose:
self.info("Receiver log shows %d of %d packets lost" % \
(numlost, lastseq))
return loss
class Experiment(object):
''' Experiment object to organize tests.
'''
def __init__(self, opt, start):
''' Initialize with opt and start time. '''
self.session = None
# node list
self.nodes = []
# WLAN network
self.net = None
self.verbose = opt.verbose
# dict from OptionParser
self.opt = opt
self.start = start
self.numping = opt.numping
self.numiperf = opt.numiperf
self.nummgen = opt.nummgen
self.logbegin()
def info(self, msg):
''' Utility method for writing output to stdout. '''
print msg
sys.stdout.flush()
self.log(msg)
def warn(self, msg):
''' Utility method for writing output to stderr. '''
print >> sys.stderr, msg
sys.stderr.flush()
self.log(msg)
def logbegin(self):
''' Start logging. '''
self.logfp = None
if not self.opt.logfile:
return
self.logfp = open(self.opt.logfile, "w")
self.log("%s begin: %s\n" % (sys.argv[0], self.start.ctime()))
self.log("%s args: %s\n" % (sys.argv[0], sys.argv[1:]))
(sysname, rel, ver, machine, nodename) = os.uname()
self.log("%s %s %s %s on %s" % (sysname, rel, ver, machine, nodename))
def logend(self):
''' End logging. '''
if not self.logfp:
return
end = datetime.datetime.now()
self.log("%s end: %s (%s)\n" % \
(sys.argv[0], end.ctime(), end - self.start))
self.logfp.flush()
self.logfp.close()
self.logfp = None
def log(self, msg):
''' Write to the log file, if any. '''
if not self.logfp:
return
print >> self.logfp, msg
def reset(self):
''' Prepare for another experiment run.
'''
if self.session:
self.session.shutdown()
del self.session
self.session = None
self.nodes = []
self.net = None
def createbridgedsession(self, numnodes, verbose = False):
''' Build a topology consisting of the given number of LxcNodes
connected to a WLAN.
'''
# IP subnet
prefix = ipaddr.IPv4Prefix("10.0.0.0/16")
self.session = pycore.Session()
# emulated network
self.net = self.session.addobj(cls = pycore.nodes.WlanNode,
name = "wlan1")
prev = None
for i in xrange(1, numnodes + 1):
addr = "%s/%s" % (prefix.addr(i), 32)
tmp = self.session.addobj(cls = pycore.nodes.CoreNode, objid = i,
name = "n%d" % i)
tmp.newnetif(self.net, [addr])
self.nodes.append(tmp)
self.session.services.addservicestonode(tmp, "router",
"IPForward", self.verbose)
self.session.services.bootnodeservices(tmp)
self.staticroutes(i, prefix, numnodes)
# link each node in a chain, with the previous node
if prev:
self.net.link(prev.netif(0), tmp.netif(0))
prev = tmp
def createemanesession(self, numnodes, verbose = False, cls = None,
values = None):
''' Build a topology consisting of the given number of LxcNodes
connected to an EMANE WLAN.
'''
prefix = ipaddr.IPv4Prefix("10.0.0.0/16")
self.session = pycore.Session()
self.session.master = True
self.session.location.setrefgeo(47.57917,-122.13232,2.00000)
self.session.location.refscale = 150.0
self.session.cfg['emane_models'] = "RfPipe, Ieee80211abg, Bypass"
self.session.emane.loadmodels()
self.net = self.session.addobj(cls = pycore.nodes.EmaneNode,
objid = numnodes + 1, name = "wlan1")
self.net.verbose = verbose
#self.session.emane.addobj(self.net)
for i in xrange(1, numnodes + 1):
addr = "%s/%s" % (prefix.addr(i), 32)
tmp = self.session.addobj(cls = pycore.nodes.CoreNode, objid = i,
name = "n%d" % i)
#tmp.setposition(i * 20, 50, None)
tmp.setposition(50, 50, None)
tmp.newnetif(self.net, [addr])
self.nodes.append(tmp)
self.session.services.addservicestonode(tmp, "router",
"IPForward", self.verbose)
if values is None:
values = cls.getdefaultvalues()
self.session.emane.setconfig(self.net.objid, cls._name, values)
self.session.emane.startup()
self.info("waiting %s sec (TAP bring-up)" % 2)
time.sleep(2)
for i in xrange(1, numnodes + 1):
tmp = self.nodes[i-1]
self.session.services.bootnodeservices(tmp)
self.staticroutes(i, prefix, numnodes)
def setnodes(self):
''' Set the sender and receiver nodes for use in this experiment,
along with the address of the receiver to be used.
'''
self.firstnode = self.nodes[0]
self.lastnode = self.nodes[-1]
self.lastaddr = self.lastnode.netif(0).addrlist[0].split('/')[0]
def staticroutes(self, i, prefix, numnodes):
''' Add static routes on node number i to the other nodes in the chain.
'''
routecmd = ["/sbin/ip", "route", "add"]
node = self.nodes[i-1]
neigh_left = ""
neigh_right = ""
# add direct interface routes first
if i > 1:
neigh_left = "%s" % prefix.addr(i - 1)
cmd = routecmd + [neigh_left, "dev", node.netif(0).name]
(status, result) = node.cmdresult(cmd)
if status != 0:
self.warn("failed to add interface route: %s" % cmd)
if i < numnodes:
neigh_right = "%s" % prefix.addr(i + 1)
cmd = routecmd + [neigh_right, "dev", node.netif(0).name]
(status, result) = node.cmdresult(cmd)
if status != 0:
self.warn("failed to add interface route: %s" % cmd)
# add static routes to all other nodes via left/right neighbors
for j in xrange(1, numnodes + 1):
if abs(j - i) < 2:
continue
addr = "%s" % prefix.addr(j)
if j < i:
gw = neigh_left
else:
gw = neigh_right
cmd = routecmd + [addr, "via", gw]
(status, result) = node.cmdresult(cmd)
if status != 0:
self.warn("failed to add route: %s" % cmd)
def setpathloss(self, numnodes):
''' Send EMANE pathloss events to connect all NEMs in a chain.
'''
service = emaneeventservice.EventService()
e = emaneeventpathloss.EventPathloss(1)
for i in xrange(1, numnodes + 1):
rxnem = i
# inform rxnem that it can hear node to the left with 10dB noise
txnem = rxnem - 1
e.set(0, txnem, 10.0, 10.0)
if txnem > 0:
service.publish(emaneeventpathloss.EVENT_ID,
emaneeventservice.PLATFORMID_ANY, rxnem,
emaneeventservice.COMPONENTID_ANY, e.export())
# inform rxnem that it can hear node to the right with 10dB noise
txnem = rxnem + 1
e.set(0, txnem, 10.0, 10.0)
if txnem <= numnodes:
service.publish(emaneeventpathloss.EVENT_ID,
emaneeventservice.PLATFORMID_ANY, rxnem,
emaneeventservice.COMPONENTID_ANY, e.export())
def setneteffects(self, bw = None, delay = None):
''' Set link effects for all interfaces attached to the network node.
'''
if not self.net:
self.warn("failed to set effects: no network node")
return
for netif in self.net.netifs():
self.net.linkconfig(netif, bw = bw, delay = delay)
def runalltests(self, title=""):
''' Convenience helper to run all defined experiment tests.
If tests are run multiple times, this returns the average of
those runs.
'''
duration = self.opt.duration
rate = self.opt.rate
if len(title) > 0:
self.info("----- running %s tests (duration=%s, rate=%s) -----" % \
(title, duration, rate))
(latency, mdev, throughput, cpu, loss) = (0,0,0,0,0)
self.info("number of runs: ping=%d, iperf=%d, mgen=%d" % \
(self.numping, self.numiperf, self.nummgen))
if self.numping > 0:
(latency, mdev) = self.pingtest(count=self.numping)
if self.numiperf > 0:
throughputs = []
for i in range(1, self.numiperf + 1):
throughput = self.iperftest(time=duration)
if self.numiperf > 1:
throughputs += throughput
time.sleep(1) # iperf is very CPU intensive
if self.numiperf > 1:
throughput = sum(throughputs) / len(throughputs)
self.info("throughputs=%s" % ["%.2f" % v for v in throughputs])
if self.nummgen > 0:
cpus = []
losses = []
for i in range(1, self.nummgen + 1):
(cpu, loss) = self.cputest(time=duration, rate=rate)
if self.nummgen > 1:
cpus += cpu,
losses += loss,
if self.nummgen > 1:
cpu = sum(cpus) / len(cpus)
loss = sum(losses) / len(losses)
self.info("cpus=%s" % ["%.2f" % v for v in cpus])
self.info("losses=%s" % ["%.2f" % v for v in losses])
return (latency, mdev, throughput, cpu, loss)
def pingtest(self, count=50):
''' Ping through a chain of nodes and report the average latency.
'''
p = PingCmd(node=self.firstnode, verbose=self.verbose,
addr = self.lastaddr, count=count, interval=0.1).run()
(latency, mdev) = p
self.info("latency (ms): %.03f, %.03f" % (latency, mdev))
return p
def iperftest(self, time=10):
''' Run iperf through a chain of nodes and report the maximum
throughput.
'''
bps = IperfCmd(node=self.lastnode, client_node=self.firstnode,
verbose=False, addr=self.lastaddr, time=time).run()
self.info("throughput (bps): %s" % bps)
return bps
def cputest(self, time=10, rate=512):
''' Run MGEN through a chain of nodes and report the CPU usage and
percent of lost packets. Rate is in kbps.
'''
if self.verbose:
self.info("%s initial test ping (max 1 second)..." % \
self.firstnode.name)
(status, result) = self.firstnode.cmdresult(["ping", "-q", "-c", "1",
"-w", "1", self.lastaddr])
if status != 0:
self.warn("initial ping from %s to %s failed! result:\n%s" % \
(self.firstnode.name, self.lastaddr, result))
return (0.0, 0.0)
lines = readstat()
cpustart = getcputimes(lines[0])
loss = MgenCmd(node=self.lastnode, client_node=self.firstnode,
verbose=False, addr=self.lastaddr,
time=time, rate=rate).run()
lines = readstat()
cpuend = getcputimes(lines[0])
percent = calculatecpu(cpustart, cpuend)
self.info("CPU usage (%%): %.02f, %.02f loss" % (percent, loss))
return percent, loss
def main():
''' Main routine when running from command-line.
'''
usagestr = "usage: %prog [-h] [options] [args]"
parser = optparse.OptionParser(usage = usagestr)
parser.set_defaults(numnodes = 10, delay = 3, duration = 10, rate = 512,
verbose = False,
numping = 50, numiperf = 1, nummgen = 1)
parser.add_option("-d", "--delay", dest = "delay", type = float,
help = "wait time before testing")
parser.add_option("-l", "--logfile", dest = "logfile", type = str,
help = "log detailed output to the specified file")
parser.add_option("-n", "--numnodes", dest = "numnodes", type = int,
help = "number of nodes")
parser.add_option("-r", "--rate", dest = "rate", type = float,
help = "kbps rate to use for MGEN CPU tests")
parser.add_option("--numping", dest = "numping", type = int,
help = "number of ping latency test runs")
parser.add_option("--numiperf", dest = "numiperf", type = int,
help = "number of iperf throughput test runs")
parser.add_option("--nummgen", dest = "nummgen", type = int,
help = "number of MGEN CPU tests runs")
parser.add_option("-t", "--time", dest = "duration", type = int,
help = "duration in seconds of throughput and CPU tests")
parser.add_option("-v", "--verbose", dest = "verbose",
action = "store_true", help = "be more verbose")
def usage(msg = None, err = 0):
sys.stdout.write("\n")
if msg:
sys.stdout.write(msg + "\n\n")
parser.print_help()
sys.exit(err)
# parse command line opt
(opt, args) = parser.parse_args()
if opt.numnodes < 2:
usage("invalid numnodes: %s" % opt.numnodes)
if opt.delay < 0.0:
usage("invalid delay: %s" % opt.delay)
if opt.rate < 0.0:
usage("invalid rate: %s" % opt.rate)
for a in args:
sys.stderr.write("ignoring command line argument: '%s'\n" % a)
results = {}
exp = Experiment(opt = opt, start=datetime.datetime.now())
# bridged
exp.info("setting up bridged tests 1/2 no link effects")
exp.info("creating topology: numnodes = %s" % \
(opt.numnodes, ))
exp.createbridgedsession(numnodes=opt.numnodes, verbose=opt.verbose)
exp.setnodes()
exp.info("waiting %s sec (node/route bring-up)" % opt.delay)
time.sleep(opt.delay)
results['0 bridged'] = exp.runalltests("bridged")
exp.info("done; elapsed time: %s" % (datetime.datetime.now() - exp.start))
# bridged with netem
exp.info("setting up bridged tests 2/2 with netem")
exp.setneteffects(bw=54000000, delay=0)
exp.info("waiting %s sec (queue bring-up)" % opt.delay)
results['1 netem'] = exp.runalltests("netem")
exp.info("shutting down bridged session")
exp.reset()
# EMANE bypass model
exp.info("setting up EMANE tests 1/2 with bypass model")
exp.createemanesession(numnodes=opt.numnodes, verbose=opt.verbose,
cls=EmaneBypassModel, values=None)
exp.setnodes()
exp.info("waiting %s sec (node/route bring-up)" % opt.delay)
time.sleep(opt.delay)
results['2 bypass'] = exp.runalltests("bypass")
exp.info("shutting down bypass session")
exp.reset()
exp.info("waiting %s sec (between EMANE tests)" % opt.delay)
time.sleep(opt.delay)
# EMANE RF-PIPE model: no restrictions (max datarate)
exp.info("setting up EMANE tests 2/4 with RF-PIPE model")
rfpipevals = list(EmaneRfPipeModel.getdefaultvalues())
rfpnames = EmaneRfPipeModel.getnames()
rfpipevals[ rfpnames.index('datarate') ] = '4294967295' # max value
rfpipevals[ rfpnames.index('pathlossmode') ] = '2ray'
rfpipevals[ rfpnames.index('defaultconnectivitymode') ] = '1'
exp.createemanesession(numnodes=opt.numnodes, verbose=opt.verbose,
cls=EmaneRfPipeModel, values=rfpipevals)
exp.setnodes()
exp.info("waiting %s sec (node/route bring-up)" % opt.delay)
time.sleep(opt.delay)
results['3 rfpipe'] = exp.runalltests("rfpipe")
exp.info("shutting down RF-PIPE session")
exp.reset()
# EMANE RF-PIPE model: 54M datarate
exp.info("setting up EMANE tests 3/4 with RF-PIPE model 54M")
rfpipevals = list(EmaneRfPipeModel.getdefaultvalues())
rfpnames = EmaneRfPipeModel.getnames()
rfpipevals[ rfpnames.index('datarate') ] = '54000'
# TX delay != propagation delay
#rfpipevals[ rfpnames.index('delay') ] = '5000'
rfpipevals[ rfpnames.index('pathlossmode') ] = '2ray'
rfpipevals[ rfpnames.index('defaultconnectivitymode') ] = '1'
exp.createemanesession(numnodes=opt.numnodes, verbose=opt.verbose,
cls=EmaneRfPipeModel, values=rfpipevals)
exp.setnodes()
exp.info("waiting %s sec (node/route bring-up)" % opt.delay)
time.sleep(opt.delay)
results['4 rfpipe54m'] = exp.runalltests("rfpipe54m")
exp.info("shutting down RF-PIPE session")
exp.reset()
# EMANE RF-PIPE model
exp.info("setting up EMANE tests 4/4 with RF-PIPE model pathloss")
rfpipevals = list(EmaneRfPipeModel.getdefaultvalues())
rfpnames = EmaneRfPipeModel.getnames()
rfpipevals[ rfpnames.index('datarate') ] = '54000'
rfpipevals[ rfpnames.index('pathlossmode') ] = 'pathloss'
rfpipevals[ rfpnames.index('defaultconnectivitymode') ] = '0'
exp.createemanesession(numnodes=opt.numnodes, verbose=opt.verbose,
cls=EmaneRfPipeModel, values=rfpipevals)
exp.setnodes()
exp.info("waiting %s sec (node/route bring-up)" % opt.delay)
time.sleep(opt.delay)
exp.info("sending pathloss events to govern connectivity")
exp.setpathloss(opt.numnodes)
results['5 pathloss'] = exp.runalltests("pathloss")
exp.info("shutting down RF-PIPE session")
exp.reset()
# summary of results in CSV format
exp.info("----- summary of results (%s nodes, rate=%s, duration=%s) -----" \
% (opt.numnodes, opt.rate, opt.duration))
exp.info("netname:latency,mdev,throughput,cpu,loss")
for test in sorted(results.keys()):
(latency, mdev, throughput, cpu, loss) = results[test]
exp.info("%s:%.03f,%.03f,%d,%.02f,%.02f" % \
(test, latency, mdev, throughput, cpu,loss))
exp.logend()
return exp
if __name__ == "__main__":
exp = main()

View file

@ -0,0 +1,98 @@
#!/usr/bin/python
# Copyright (c)2010-2012 the Boeing Company.
# See the LICENSE file included in this distribution.
# run iperf to measure the effective throughput between two nodes when
# n nodes are connected to a virtual wlan; run test for testsec
# and repeat for minnodes <= n <= maxnodes with a step size of
# nodestep
import optparse, sys, os, datetime
from core import pycore
from core.misc import ipaddr
from core.misc.utils import mutecall
try:
mutecall(["iperf", "-v"])
except OSError:
sys.stderr.write("ERROR: running iperf failed\n")
sys.exit(1)
def test(numnodes, testsec):
# node list
n = []
# IP subnet
prefix = ipaddr.IPv4Prefix("10.83.0.0/16")
session = pycore.Session()
# emulated network
net = session.addobj(cls = pycore.nodes.WlanNode)
for i in xrange(1, numnodes + 1):
tmp = session.addobj(cls = pycore.nodes.LxcNode, name = "n%d" % i)
tmp.newnetif(net, ["%s/%s" % (prefix.addr(i), prefix.prefixlen)])
n.append(tmp)
net.link(n[0].netif(0), n[-1].netif(0))
n[0].cmd(["iperf", "-s", "-D"])
n[-1].icmd(["iperf", "-t", str(int(testsec)), "-c", str(prefix.addr(1))])
n[0].cmd(["killall", "-9", "iperf"])
session.shutdown()
def main():
usagestr = "usage: %prog [-h] [options] [args]"
parser = optparse.OptionParser(usage = usagestr)
parser.set_defaults(minnodes = 2)
parser.add_option("-m", "--minnodes", dest = "minnodes", type = int,
help = "min number of nodes to test; default = %s" %
parser.defaults["minnodes"])
parser.set_defaults(maxnodes = 2)
parser.add_option("-n", "--maxnodes", dest = "maxnodes", type = int,
help = "max number of nodes to test; default = %s" %
parser.defaults["maxnodes"])
parser.set_defaults(testsec = 10)
parser.add_option("-t", "--testsec", dest = "testsec", type = int,
help = "test time in seconds; default = %s" %
parser.defaults["testsec"])
parser.set_defaults(nodestep = 1)
parser.add_option("-s", "--nodestep", dest = "nodestep", type = int,
help = "number of nodes step size; default = %s" %
parser.defaults["nodestep"])
def usage(msg = None, err = 0):
sys.stdout.write("\n")
if msg:
sys.stdout.write(msg + "\n\n")
parser.print_help()
sys.exit(err)
# parse command line options
(options, args) = parser.parse_args()
if options.minnodes < 2:
usage("invalid min number of nodes: %s" % options.minnodes)
if options.maxnodes < options.minnodes:
usage("invalid max number of nodes: %s" % options.maxnodes)
if options.testsec < 1:
usage("invalid test time: %s" % options.testsec)
if options.nodestep < 1:
usage("invalid node step: %s" % options.nodestep)
for a in args:
sys.stderr.write("ignoring command line argument: '%s'\n" % a)
start = datetime.datetime.now()
for i in xrange(options.minnodes, options.maxnodes + 1, options.nodestep):
print >> sys.stderr, "%s node test:" % i
test(i, options.testsec)
print >> sys.stderr, ""
print >> sys.stderr, \
"elapsed time: %s" % (datetime.datetime.now() - start)
if __name__ == "__main__":
main()

View file

@ -0,0 +1,30 @@
# -------- CUSTOMIZATION REQUIRED --------
#
# Below are sample iptables firewall rules that you can uncomment and edit.
# You can also use ip6tables rules for IPv6.
#
# start by flushing all firewall rules (so this script may be re-run)
#iptables -F
# allow traffic related to established connections
#iptables -A INPUT -m state --state ESTABLISHED,RELATED -j ACCEPT
# allow TCP packets from any source destined for 192.168.1.1
#iptables -A INPUT -s 0/0 -i eth0 -d 192.168.1.1 -p TCP -j ACCEPT
# allow OpenVPN server traffic from eth0
#iptables -A INPUT -p udp --dport 1194 -j ACCEPT
#iptables -A INPUT -i eth0 -j DROP
#iptables -A OUTPUT -p udp --sport 1194 -j ACCEPT
#iptables -A OUTPUT -o eth0 -j DROP
# allow ICMP ping traffic
#iptables -A OUTPUT -p icmp --icmp-type echo-request -j ACCEPT
#iptables -A INPUT -p icmp --icmp-type echo-reply -j ACCEPT
# allow SSH traffic
#iptables -A -p tcp -m state --state NEW -m tcp --dport 22 -j ACCEPT
# drop all other traffic coming in eth0
#iptables -A INPUT -i eth0 -j DROP

View file

@ -0,0 +1,119 @@
# -------- CUSTOMIZATION REQUIRED --------
#
# The IPsec service builds ESP tunnels between the specified peers using the
# racoon IKEv2 keying daemon. You need to provide keys and the addresses of
# peers, along with subnets to tunnel.
# directory containing the certificate and key described below
keydir=/etc/core/keys
# the name used for the "$certname.pem" x509 certificate and
# "$certname.key" RSA private key, which can be generated using openssl
certname=ipsec1
# list the public-facing IP addresses, starting with the localhost and followed
# by each tunnel peer, separated with a single space
tunnelhosts="172.16.0.1AND172.16.0.2 172.16.0.1AND172.16.2.1"
# Define T<i> where i is the index for each tunnel peer host from
# the tunnel_hosts list above (0 is localhost).
# T<i> is a list of IPsec tunnels with peer i, with a local subnet address
# followed by the remote subnet address:
# T<i>="<local>AND<remote> <local>AND<remote>"
# For example, 172.16.0.0/24 is a local network (behind this node) to be
# tunneled and 172.16.2.0/24 is a remote network (behind peer 1)
T1="172.16.3.0/24AND172.16.5.0/24"
T2="172.16.4.0/24AND172.16.5.0/24 172.16.4.0/24AND172.16.6.0/24"
# -------- END CUSTOMIZATION --------
echo "building config $PWD/ipsec.conf..."
echo "building config $PWD/ipsec.conf..." > $PWD/ipsec.log
checkip=0
if [ "$(dpkg -l | grep " sipcalc ")" = "" ]; then
echo "WARNING: ip validation disabled because package sipcalc not installed
" >> $PWD/ipsec.log
checkip=1
fi
echo "#!/usr/sbin/setkey -f
# Flush the SAD and SPD
flush;
spdflush;
# Security policies \
" > $PWD/ipsec.conf
i=0
for hostpair in $tunnelhosts; do
i=`expr $i + 1`
# parse tunnel host IP
thishost=${hostpair%%AND*}
peerhost=${hostpair##*AND}
if [ $checkip = "0" ] &&
[ "$(sipcalc "$thishost" "$peerhost" | grep ERR)" != "" ]; then
echo "ERROR: invalid host address $thishost or $peerhost \
" >> $PWD/ipsec.log
fi
# parse each tunnel addresses
tunnel_list_var_name=T$i
eval tunnels="$"$tunnel_list_var_name""
for ttunnel in $tunnels; do
lclnet=${ttunnel%%AND*}
rmtnet=${ttunnel##*AND}
if [ $checkip = "0" ] &&
[ "$(sipcalc "$lclnet" "$rmtnet"| grep ERR)" != "" ]; then
echo "ERROR: invalid tunnel address $lclnet and $rmtnet \
" >> $PWD/ipsec.log
fi
# add tunnel policies
echo "
spdadd $lclnet $rmtnet any -P out ipsec
esp/tunnel/$thishost-$peerhost/require;
spdadd $rmtnet $lclnet any -P in ipsec
esp/tunnel/$peerhost-$thishost/require; \
" >> $PWD/ipsec.conf
done
done
echo "building config $PWD/racoon.conf..."
if [ ! -e $keydir\/$certname.key ] || [ ! -e $keydir\/$certname.pem ]; then
echo "ERROR: missing certification files under $keydir \
$certname.key or $certname.pem " >> $PWD/ipsec.log
fi
echo "
path certificate \"$keydir\";
listen {
adminsock disabled;
}
remote anonymous
{
exchange_mode main;
certificate_type x509 \"$certname.pem\" \"$certname.key\";
ca_type x509 \"ca-cert.pem\";
my_identifier asn1dn;
peers_identifier asn1dn;
proposal {
encryption_algorithm 3des ;
hash_algorithm sha1;
authentication_method rsasig ;
dh_group modp768;
}
}
sainfo anonymous
{
pfs_group modp768;
lifetime time 1 hour ;
encryption_algorithm 3des, blowfish 448, rijndael ;
authentication_algorithm hmac_sha1, hmac_md5 ;
compression_algorithm deflate ;
}
" > $PWD/racoon.conf
# the setkey program is required from the ipsec-tools package
echo "running setkey -f $PWD/ipsec.conf..."
setkey -f $PWD/ipsec.conf
echo "running racoon -d -f $PWD/racoon.conf..."
racoon -d -f $PWD/racoon.conf -l racoon.log

View file

@ -0,0 +1,63 @@
# -------- CUSTOMIZATION REQUIRED --------
#
# The VPNClient service builds a VPN tunnel to the specified VPN server using
# OpenVPN software and a virtual TUN/TAP device.
# directory containing the certificate and key described below
keydir=/etc/core/keys
# the name used for a "$keyname.crt" certificate and "$keyname.key" private key.
keyname=client1
# the public IP address of the VPN server this client should connect with
vpnserver="10.0.2.10"
# optional next hop for adding a static route to reach the VPN server
nexthop="10.0.1.1"
# --------- END CUSTOMIZATION --------
# validate addresses
if [ "$(dpkg -l | grep " sipcalc ")" = "" ]; then
echo "WARNING: ip validation disabled because package sipcalc not installed
" > $PWD/vpnclient.log
else
if [ "$(sipcalc "$vpnserver" "$nexthop" | grep ERR)" != "" ]; then
echo "ERROR: invalide address $vpnserver or $nexthop \
" > $PWD/vpnclient.log
fi
fi
# validate key and certification files
if [ ! -e $keydir\/$keyname.key ] || [ ! -e $keydir\/$keyname.crt ] \
|| [ ! -e $keydir\/ca.crt ] || [ ! -e $keydir\/dh1024.pem ]; then
echo "ERROR: missing certification or key files under $keydir \
$keyname.key or $keyname.crt or ca.crt or dh1024.pem" >> $PWD/vpnclient.log
fi
# if necessary, add a static route for reaching the VPN server IP via the IF
vpnservernet=${vpnserver%.*}.0/24
if [ "$nexthop" != "" ]; then
/sbin/ip route add $vpnservernet via $nexthop
fi
# create openvpn client.conf
(
cat << EOF
client
dev tun
proto udp
remote $vpnserver 1194
nobind
ca $keydir/ca.crt
cert $keydir/$keyname.crt
key $keydir/$keyname.key
dh $keydir/dh1024.pem
cipher AES-256-CBC
log $PWD/openvpn-client.log
verb 4
daemon
EOF
) > client.conf
openvpn --config client.conf

View file

@ -0,0 +1,147 @@
# -------- CUSTOMIZATION REQUIRED --------
#
# The VPNServer service sets up the OpenVPN server for building VPN tunnels
# that allow access via TUN/TAP device to private networks.
#
# note that the IPForward and DefaultRoute services should be enabled
# directory containing the certificate and key described below, in addition to
# a CA certificate and DH key
keydir=/etc/core/keys
# the name used for a "$keyname.crt" certificate and "$keyname.key" private key.
keyname=server2
# the VPN subnet address from which the client VPN IP (for the TUN/TAP)
# will be allocated
vpnsubnet=10.0.200.0
# public IP address of this vpn server (same as VPNClient vpnserver= setting)
vpnserver=10.0.2.10
# optional list of private subnets reachable behind this VPN server
# each subnet and next hop is separated by a space
# "<subnet1>,<nexthop1> <subnet2>,<nexthop2> ..."
privatenets="10.0.11.0,10.0.10.1 10.0.12.0,10.0.10.1"
# optional list of VPN clients, for statically assigning IP addresses to
# clients; also, an optional client subnet can be specified for adding static
# routes via the client
# Note: VPN addresses x.x.x.0-3 are reserved
# "<keyname>,<vpnIP>,<subnetIP> <keyname>,<vpnIP>,<subnetIP> ..."
vpnclients="client1KeyFilename,10.0.200.5,10.0.0.0 client2KeyFilename,,"
# NOTE: you may need to enable the StaticRoutes service on nodes within the
# private subnet, in order to have routes back to the client.
# /sbin/ip ro add <vpnsubnet>/24 via <vpnServerRemoteInterface>
# /sbin/ip ro add <vpnClientSubnet>/24 via <vpnServerRemoteInterface>
# -------- END CUSTOMIZATION --------
echo > $PWD/vpnserver.log
rm -f -r $PWD/ccd
# validate key and certification files
if [ ! -e $keydir\/$keyname.key ] || [ ! -e $keydir\/$keyname.crt ] \
|| [ ! -e $keydir\/ca.crt ] || [ ! -e $keydir\/dh1024.pem ]; then
echo "ERROR: missing certification or key files under $keydir \
$keyname.key or $keyname.crt or ca.crt or dh1024.pem" >> $PWD/vpnserver.log
fi
# validate configuration IP addresses
checkip=0
if [ "$(dpkg -l | grep " sipcalc ")" = "" ]; then
echo "WARNING: ip validation disabled because package sipcalc not installed\
" >> $PWD/vpnserver.log
checkip=1
else
if [ "$(sipcalc "$vpnsubnet" "$vpnserver" | grep ERR)" != "" ]; then
echo "ERROR: invalid vpn subnet or server address \
$vpnsubnet or $vpnserver " >> $PWD/vpnserver.log
fi
fi
# create client vpn ip pool file
(
cat << EOF
EOF
)> $PWD/ippool.txt
# create server.conf file
(
cat << EOF
# openvpn server config
local $vpnserver
server $vpnsubnet 255.255.255.0
push redirect-gateway def1
EOF
)> $PWD/server.conf
# add routes to VPN server private subnets, and push these routes to clients
for privatenet in $privatenets; do
if [ $privatenet != "" ]; then
net=${privatenet%%,*}
nexthop=${privatenet##*,}
if [ $checkip = "0" ] &&
[ "$(sipcalc "$net" "$nexthop" | grep ERR)" != "" ]; then
echo "ERROR: invalid vpn server private net address \
$net or $nexthop " >> $PWD/vpnserver.log
fi
echo push route $net 255.255.255.0 >> $PWD/server.conf
/sbin/ip ro add $net/24 via $nexthop
/sbin/ip ro add $vpnsubnet/24 via $nexthop
fi
done
# allow subnet through this VPN, one route for each client subnet
for client in $vpnclients; do
if [ $client != "" ]; then
cSubnetIP=${client##*,}
cVpnIP=${client#*,}
cVpnIP=${cVpnIP%%,*}
cKeyFilename=${client%%,*}
if [ "$cSubnetIP" != "" ]; then
if [ $checkip = "0" ] &&
[ "$(sipcalc "$cSubnetIP" "$cVpnIP" | grep ERR)" != "" ]; then
echo "ERROR: invalid vpn client and subnet address \
$cSubnetIP or $cVpnIP " >> $PWD/vpnserver.log
fi
echo route $cSubnetIP 255.255.255.0 >> $PWD/server.conf
if ! test -d $PWD/ccd; then
mkdir -p $PWD/ccd
echo client-config-dir $PWD/ccd >> $PWD/server.conf
fi
if test -e $PWD/ccd/$cKeyFilename; then
echo iroute $cSubnetIP 255.255.255.0 >> $PWD/ccd/$cKeyFilename
else
echo iroute $cSubnetIP 255.255.255.0 > $PWD/ccd/$cKeyFilename
fi
fi
if [ "$cVpnIP" != "" ]; then
echo $cKeyFilename,$cVpnIP >> $PWD/ippool.txt
fi
fi
done
(
cat << EOF
keepalive 10 120
ca $keydir/ca.crt
cert $keydir/$keyname.crt
key $keydir/$keyname.key
dh $keydir/dh1024.pem
cipher AES-256-CBC
status /var/log/openvpn-status.log
log /var/log/openvpn-server.log
ifconfig-pool-linear
ifconfig-pool-persist $PWD/ippool.txt
port 1194
proto udp
dev tun
verb 4
daemon
EOF
)>> $PWD/server.conf
# start vpn server
openvpn --config server.conf

45
daemon/examples/stopsession.py Executable file
View file

@ -0,0 +1,45 @@
#!/usr/bin/env python
# (c)2010-2012 the Boeing Company
# author: Jeff Ahrenholz <jeffrey.m.ahrenholz@boeing.com>
#
# List and stop CORE sessions from the command line.
#
import socket, optparse
from core.constants import *
from core.api import coreapi
def main():
parser = optparse.OptionParser(usage = "usage: %prog [-l] <sessionid>")
parser.add_option("-l", "--list", dest = "list", action = "store_true",
help = "list running sessions")
(options, args) = parser.parse_args()
if options.list is True:
num = '0'
flags = coreapi.CORE_API_STR_FLAG
else:
num = args[0]
flags = coreapi.CORE_API_DEL_FLAG
tlvdata = coreapi.CoreSessionTlv.pack(coreapi.CORE_TLV_SESS_NUMBER, num)
msg = coreapi.CoreSessionMessage.pack(flags, tlvdata)
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
sock.connect(('localhost', coreapi.CORE_API_PORT))
sock.send(msg)
# receive and print a session list
if options.list is True:
hdr = sock.recv(coreapi.CoreMessage.hdrsiz)
msgtype, msgflags, msglen = coreapi.CoreMessage.unpackhdr(hdr)
data = ""
if msglen:
data = sock.recv(msglen)
msg = coreapi.CoreMessage(msgflags, hdr, data)
sessions = msg.gettlv(coreapi.CORE_TLV_SESS_NUMBER)
print "sessions:", sessions
sock.close()
if __name__ == "__main__":
main()