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,6 @@
"""Services
Services available to nodes can be put in this directory. Everything listed in
__all__ is automatically loaded by the main core module.
"""
__all__ = ["quagga", "nrl", "xorp", "bird", "utility", "security", "ucarp"]

View file

@ -0,0 +1,249 @@
#
# CORE
# Copyright (c)2012 Jean-Tiare Le Bigot.
# See the LICENSE file included in this distribution.
#
# authors: Jean-Tiare Le Bigot <admin@jtlebi.fr>
# Jeff Ahrenholz <jeffrey.m.ahrenholz@boeing.com>
#
'''
bird.py: defines routing services provided by the BIRD Internet Routing Daemon.
'''
import os
from core.service import CoreService, addservice
from core.misc.ipaddr import IPv4Prefix
from core.constants import *
class Bird(CoreService):
''' Bird router support
'''
_name = "bird"
_group = "BIRD"
_depends = ()
_dirs = ("/etc/bird",)
_configs = ("/etc/bird/bird.conf",)
_startindex = 35
_startup = ("bird -c %s" % (_configs[0]),)
_shutdown = ("killall bird", )
_validate = ("pidof bird", )
@classmethod
def generateconfig(cls, node, filename, services):
''' Return the bird.conf file contents.
'''
if filename == cls._configs[0]:
return cls.generateBirdConf(node, services)
else:
raise ValueError
@staticmethod
def routerid(node):
''' Helper to return the first IPv4 address of a node as its router ID.
'''
for ifc in node.netifs():
if hasattr(ifc, 'control') and ifc.control == True:
continue
for a in ifc.addrlist:
if a.find(".") >= 0:
return a .split('/') [0]
#raise ValueError, "no IPv4 address found for router ID"
return "0.0.0.0"
@classmethod
def generateBirdConf(cls, node, services):
''' Returns configuration file text. Other services that depend on bird
will have generatebirdifcconfig() and generatebirdconfig()
hooks that are invoked here.
'''
cfg = """\
/* Main configuration file for BIRD. This is ony a template,
* you will *need* to customize it according to your needs
* Beware that only double quotes \'"\' are valid. No singles. */
log "/var/log/%s.log" all;
#debug protocols all;
#debug commands 2;
router id %s; # Mandatory for IPv6, may be automatic for IPv4
protocol kernel {
persist; # Don\'t remove routes on BIRD shutdown
scan time 200; # Scan kernel routing table every 200 seconds
export all;
import all;
}
protocol device {
scan time 10; # Scan interfaces every 10 seconds
}
""" % (cls._name, cls.routerid(node))
# Generate protocol specific configurations
for s in services:
if cls._name not in s._depends:
continue
cfg += s.generatebirdconfig(node)
return cfg
class BirdService(CoreService):
''' Parent class for Bird services. Defines properties and methods
common to Bird's routing daemons.
'''
_name = "BirdDaemon"
_group = "BIRD"
_depends = ("bird", )
_dirs = ()
_configs = ()
_startindex = 40
_startup = ()
_shutdown = ()
_meta = "The config file for this service can be found in the bird service."
@classmethod
def generatebirdconfig(cls, node):
return ""
@classmethod
def generatebirdifcconfig(cls, node):
''' Use only bare interfaces descriptions in generated protocol
configurations. This has the slight advantage of being the same
everywhere.
'''
cfg = ""
for ifc in node.netifs():
if hasattr(ifc, 'control') and ifc.control == True: continue
cfg += ' interface "%s";\n'% ifc.name
return cfg
class BirdBgp(BirdService):
'''BGP BIRD Service (configuration generation)'''
_name = "BIRD_BGP"
_custom_needed = True
@classmethod
def generatebirdconfig(cls, node):
return """
/* This is a sample config that should be customized with appropriate AS numbers
* and peers; add one section like this for each neighbor */
protocol bgp {
local as 65000; # Customize your AS number
neighbor 198.51.100.130 as 64496; # Customize neighbor AS number && IP
export filter { # We use non-trivial export rules
# This is an example. You should advertise only *your routes*
if (source = RTS_DEVICE) || (source = RTS_OSPF) then {
# bgp_community.add((65000,64501)); # Assign our community
accept;
}
reject;
};
import all;
}
"""
class BirdOspf(BirdService):
'''OSPF BIRD Service (configuration generation)'''
_name = "BIRD_OSPFv2"
@classmethod
def generatebirdconfig(cls, node):
cfg = 'protocol ospf {\n'
cfg += ' export filter {\n'
cfg += ' if source = RTS_BGP then {\n'
cfg += ' ospf_metric1 = 100;\n'
cfg += ' accept;\n'
cfg += ' }\n'
cfg += ' accept;\n'
cfg += ' };\n'
cfg += ' area 0.0.0.0 {\n'
cfg += cls.generatebirdifcconfig(node)
cfg += ' };\n'
cfg += '}\n\n'
return cfg
class BirdRadv(BirdService):
'''RADV BIRD Service (configuration generation)'''
_name = "BIRD_RADV"
@classmethod
def generatebirdconfig(cls, node):
cfg = '/* This is a sample config that must be customized */\n'
cfg += 'protocol radv {\n'
cfg += ' # auto configuration on all interfaces\n'
cfg += cls.generatebirdifcconfig(node)
cfg += ' # Advertise DNS\n'
cfg += ' rdnss {\n'
cfg += '# lifetime mult 10;\n'
cfg += '# lifetime mult 10;\n'
cfg += '# ns 2001:0DB8:1234::11;\n'
cfg += '# ns 2001:0DB8:1234::11;\n'
cfg += '# ns 2001:0DB8:1234::12;\n'
cfg += '# ns 2001:0DB8:1234::12;\n'
cfg += ' };\n'
cfg += '}\n\n'
return cfg
class BirdRip(BirdService):
'''RIP BIRD Service (configuration generation)'''
_name = "BIRD_RIP"
@classmethod
def generatebirdconfig(cls, node):
cfg = 'protocol rip {\n'
cfg += ' period 10;\n'
cfg += ' garbage time 60;\n'
cfg += cls.generatebirdifcconfig(node)
cfg += ' honor neighbor;\n'
cfg += ' authentication none;\n'
cfg += ' import all;\n'
cfg += ' export all;\n'
cfg += '}\n\n'
return cfg
class BirdStatic(BirdService):
'''Static Bird Service (configuration generation)'''
_name = "BIRD_static"
_custom_needed = True
@classmethod
def generatebirdconfig(cls, node):
cfg = '/* This is a sample config that must be customized */\n'
cfg += 'protocol static {\n'
cfg += '# route 0.0.0.0/0 via 198.51.100.130; # Default route. Do NOT advertise on BGP !\n'
cfg += '# route 203.0.113.0/24 reject; # Sink route\n'
cfg += '# route 10.2.0.0/24 via "arc0"; # Secondary network\n'
cfg += '}\n\n'
return cfg
# Register all protocols
addservice(Bird)
addservice(BirdOspf)
addservice(BirdBgp)
#addservice(BirdRadv) # untested
addservice(BirdRip)
addservice(BirdStatic)

191
daemon/core/services/nrl.py Normal file
View file

@ -0,0 +1,191 @@
#
# CORE
# Copyright (c)2010-2012 the Boeing Company.
# See the LICENSE file included in this distribution.
#
# author: Jeff Ahrenholz <jeffrey.m.ahrenholz@boeing.com>
#
'''
nrl.py: defines services provided by NRL protolib tools hosted here:
http://cs.itd.nrl.navy.mil/products/
'''
from core.service import CoreService, addservice
from core.misc.ipaddr import IPv4Prefix
class NrlService(CoreService):
''' Parent class for NRL services. Defines properties and methods
common to NRL's routing daemons.
'''
_name = "NRLDaemon"
_group = "Routing"
_depends = ()
_dirs = ()
_configs = ()
_startindex = 45
_startup = ()
_shutdown = ()
@classmethod
def generateconfig(cls, node, filename, services):
return ""
@staticmethod
def firstipv4prefix(node, prefixlen=24):
''' Similar to QuaggaService.routerid(). Helper to return the first IPv4
prefix of a node, using the supplied prefix length. This ignores the
interface's prefix length, so e.g. '/32' can turn into '/24'.
'''
for ifc in node.netifs():
if hasattr(ifc, 'control') and ifc.control == True:
continue
for a in ifc.addrlist:
if a.find(".") >= 0:
addr = a.split('/')[0]
pre = IPv4Prefix("%s/%s" % (addr, prefixlen))
return str(pre)
#raise ValueError, "no IPv4 address found"
return "0.0.0.0/%s" % prefixlen
class NrlNhdp(NrlService):
''' NeighborHood Discovery Protocol for MANET networks.
'''
_name = "NHDP"
_startup = ("nrlnhdp", )
_shutdown = ("killall nrlnhdp", )
_validate = ("pidof nrlnhdp", )
@classmethod
def getstartup(cls, node, services):
''' Generate the appropriate command-line based on node interfaces.
'''
cmd = cls._startup[0]
cmd += " -l /var/log/nrlnhdp.log"
cmd += " -rpipe %s_nhdp" % node.name
servicenames = map(lambda x: x._name, services)
if "SMF" in servicenames:
cmd += " -flooding ecds"
cmd += " -smfClient %s_smf" % node.name
netifs = filter(lambda x: not getattr(x, 'control', False), \
node.netifs())
if len(netifs) > 0:
interfacenames = map(lambda x: x.name, netifs)
cmd += " -i "
cmd += " -i ".join(interfacenames)
return (cmd, )
addservice(NrlNhdp)
class NrlSmf(NrlService):
''' Simplified Multicast Forwarding for MANET networks.
'''
_name = "SMF"
_startup = ("nrlsmf", )
_shutdown = ("killall nrlsmf", )
_validate = ("pidof nrlsmf", )
@classmethod
def getstartup(cls, node, services):
''' Generate the appropriate command-line based on node interfaces.
'''
cmd = cls._startup[0]
cmd += " instance %s_smf" % node.name
servicenames = map(lambda x: x._name, services)
netifs = filter(lambda x: not getattr(x, 'control', False), \
node.netifs())
if len(netifs) == 0:
return ()
if "arouted" in servicenames:
cmd += " tap %s_tap" % (node.name,)
cmd += " unicast %s" % cls.firstipv4prefix(node, 24)
cmd += " push lo,%s resequence on" % netifs[0].name
if len(netifs) > 0:
if "NHDP" in servicenames:
cmd += " ecds "
elif "OLSR" in servicenames:
cmd += " smpr "
else:
cmd += " cf "
interfacenames = map(lambda x: x.name, netifs)
cmd += ",".join(interfacenames)
cmd += " hash MD5"
cmd += " log /var/log/nrlsmf.log"
return (cmd, )
addservice(NrlSmf)
class NrlOlsr(NrlService):
''' Optimized Link State Routing protocol for MANET networks.
'''
_name = "OLSR"
_startup = ("nrlolsrd", )
_shutdown = ("killall nrlolsrd", )
_validate = ("pidof nrlolsrd", )
@classmethod
def getstartup(cls, node, services):
''' Generate the appropriate command-line based on node interfaces.
'''
cmd = cls._startup[0]
# are multiple interfaces supported? No.
netifs = list(node.netifs())
if len(netifs) > 0:
ifc = netifs[0]
cmd += " -i %s" % ifc.name
cmd += " -l /var/log/nrlolsrd.log"
cmd += " -rpipe %s_olsr" % node.name
servicenames = map(lambda x: x._name, services)
if "SMF" in servicenames and not "NHDP" in servicenames:
cmd += " -flooding s-mpr"
cmd += " -smfClient %s_smf" % node.name
if "zebra" in servicenames:
cmd += " -z"
return (cmd, )
addservice(NrlOlsr)
class Arouted(NrlService):
''' Adaptive Routing
'''
_name = "arouted"
_configs = ("startarouted.sh", )
_startindex = NrlService._startindex + 10
_startup = ("sh startarouted.sh", )
_shutdown = ("pkill arouted", )
_validate = ("pidof arouted", )
@classmethod
def generateconfig(cls, node, filename, services):
''' Return the Quagga.conf or quaggaboot.sh file contents.
'''
cfg = """
#!/bin/sh
for f in "/tmp/%s_smf"; do
count=1
until [ -e "$f" ]; do
if [ $count -eq 10 ]; then
echo "ERROR: nrlmsf pipe not found: $f" >&2
exit 1
fi
sleep 0.1
count=$(($count + 1))
done
done
""" % (node.name)
cfg += "ip route add %s dev lo\n" % cls.firstipv4prefix(node, 24)
cfg += "arouted instance %s_smf tap %s_tap" % (node.name, node.name)
cfg += " stability 10" # seconds to consider a new route valid
cfg += " 2>&1 > /var/log/arouted.log &\n\n"
return cfg
# experimental
#addservice(Arouted)

View file

@ -0,0 +1,589 @@
#
# CORE
# Copyright (c)2010-2012 the Boeing Company.
# See the LICENSE file included in this distribution.
#
# author: Jeff Ahrenholz <jeffrey.m.ahrenholz@boeing.com>
#
'''
quagga.py: defines routing services provided by Quagga.
'''
import os
if os.uname()[0] == "Linux":
from core.netns import nodes
elif os.uname()[0] == "FreeBSD":
from core.bsd import nodes
from core.service import CoreService, addservice
from core.misc.ipaddr import IPv4Prefix, isIPv4Address, isIPv6Address
from core.api import coreapi
from core.constants import *
QUAGGA_USER="root"
QUAGGA_GROUP="root"
if os.uname()[0] == "FreeBSD":
QUAGGA_GROUP="wheel"
class Zebra(CoreService):
'''
'''
_name = "zebra"
_group = "Quagga"
_depends = ("vtysh", )
_dirs = ("/usr/local/etc/quagga", "/var/run/quagga")
_configs = ("/usr/local/etc/quagga/Quagga.conf",
"quaggaboot.sh","/usr/local/etc/quagga/vtysh.conf")
_startindex = 35
_startup = ("sh quaggaboot.sh zebra",)
_shutdown = ("killall zebra", )
_validate = ("pidof zebra", )
@classmethod
def generateconfig(cls, node, filename, services):
''' Return the Quagga.conf or quaggaboot.sh file contents.
'''
if filename == cls._configs[0]:
return cls.generateQuaggaConf(node, services)
elif filename == cls._configs[1]:
return cls.generateQuaggaBoot(node, services)
elif filename == cls._configs[2]:
return cls.generateVtyshConf(node, services)
else:
raise ValueError
@classmethod
def generateVtyshConf(cls, node, services):
''' Returns configuration file text.
'''
return "service integrated-vtysh-config"
@classmethod
def generateQuaggaConf(cls, node, services):
''' Returns configuration file text. Other services that depend on zebra
will have generatequaggaifcconfig() and generatequaggaconfig()
hooks that are invoked here.
'''
# we could verify here that filename == Quagga.conf
cfg = ""
for ifc in node.netifs():
cfg += "interface %s\n" % ifc.name
# include control interfaces in addressing but not routing daemons
if hasattr(ifc, 'control') and ifc.control == True:
cfg += " "
cfg += "\n ".join(map(cls.addrstr, ifc.addrlist))
cfg += "\n"
continue
cfgv4 = ""
cfgv6 = ""
want_ipv4 = False
want_ipv6 = False
for s in services:
if cls._name not in s._depends:
continue
ifccfg = s.generatequaggaifcconfig(node, ifc)
if s._ipv4_routing:
want_ipv4 = True
if s._ipv6_routing:
want_ipv6 = True
cfgv6 += ifccfg
else:
cfgv4 += ifccfg
if want_ipv4:
ipv4list = filter(lambda x: isIPv4Address(x.split('/')[0]),
ifc.addrlist)
cfg += " "
cfg += "\n ".join(map(cls.addrstr, ipv4list))
cfg += "\n"
cfg += cfgv4
if want_ipv6:
ipv6list = filter(lambda x: isIPv6Address(x.split('/')[0]),
ifc.addrlist)
cfg += " "
cfg += "\n ".join(map(cls.addrstr, ipv6list))
cfg += "\n"
cfg += cfgv6
cfg += "!\n"
for s in services:
if cls._name not in s._depends:
continue
cfg += s.generatequaggaconfig(node)
return cfg
@staticmethod
def addrstr(x):
''' helper for mapping IP addresses to zebra config statements
'''
if x.find(".") >= 0:
return "ip address %s" % x
elif x.find(":") >= 0:
return "ipv6 address %s" % x
else:
raise Value, "invalid address: %s", x
@classmethod
def generateQuaggaBoot(cls, node, services):
''' Generate a shell script used to boot the Quagga daemons.
'''
try:
quagga_bin_search = node.session.cfg['quagga_bin_search']
quagga_sbin_search = node.session.cfg['quagga_sbin_search']
except KeyError:
quagga_bin_search = '"/usr/local/bin /usr/bin /usr/lib/quagga"'
quagga_sbin_search = '"/usr/local/sbin /usr/sbin /usr/lib/quagga"'
return """\
#!/bin/sh
# auto-generated by zebra service (quagga.py)
QUAGGA_CONF=%s
QUAGGA_SBIN_SEARCH=%s
QUAGGA_BIN_SEARCH=%s
QUAGGA_STATE_DIR=%s
QUAGGA_USER=%s
QUAGGA_GROUP=%s
searchforprog()
{
prog=$1
searchpath=$@
ret=
for p in $searchpath; do
if [ -x $p/$prog ]; then
ret=$p
break
fi
done
echo $ret
}
confcheck()
{
CONF_DIR=`dirname $QUAGGA_CONF`
# if /etc/quagga exists, point /etc/quagga/Quagga.conf -> CONF_DIR
if [ "$CONF_DIR" != "/etc/quagga" ] && [ -d /etc/quagga ] && [ ! -e /etc/quagga/Quagga.conf ]; then
ln -s $CONF_DIR/Quagga.conf /etc/quagga/Quagga.conf
fi
# if /etc/quagga exists, point /etc/quagga/vtysh.conf -> CONF_DIR
if [ "$CONF_DIR" != "/etc/quagga" ] && [ -d /etc/quagga ] && [ ! -e /etc/quagga/vtysh.conf ]; then
ln -s $CONF_DIR/vtysh.conf /etc/quagga/vtysh.conf
fi
}
waitforvtyfiles()
{
for f in "$@"; do
count=1
until [ -e $QUAGGA_STATE_DIR/$f ]; do
if [ $count -eq 10 ]; then
echo "ERROR: vty file not found: $QUAGGA_STATE_DIR/$f" >&2
return 1
fi
sleep 0.1
count=$(($count + 1))
done
done
}
bootdaemon()
{
QUAGGA_SBIN_DIR=$(searchforprog $1 $QUAGGA_SBIN_SEARCH)
if [ "z$QUAGGA_SBIN_DIR" = "z" ]; then
echo "ERROR: Quagga's '$1' daemon not found in search path:"
echo " $QUAGGA_SBIN_SEARCH"
return 1
fi
if [ "$1" != "zebra" ]; then
waitforvtyfiles zebra.vty
fi
$QUAGGA_SBIN_DIR/$1 -u $QUAGGA_USER -g $QUAGGA_GROUP -d
}
bootvtysh()
{
QUAGGA_BIN_DIR=$(searchforprog $1 $QUAGGA_BIN_SEARCH)
if [ "z$QUAGGA_BIN_DIR" = "z" ]; then
echo "ERROR: Quagga's '$1' daemon not found in search path:"
echo " $QUAGGA_SBIN_SEARCH"
return 1
fi
vtyfiles="zebra.vty"
for r in rip ripng ospf6 ospf bgp babel; do
if grep -q "^router \<${r}\>" $QUAGGA_CONF; then
vtyfiles="$vtyfiles ${r}d.vty"
fi
done
# wait for Quagga daemon vty files to appear before invoking vtysh
waitforvtyfiles $vtyfiles
$QUAGGA_BIN_DIR/vtysh -b
}
confcheck
if [ "x$1" = "x" ]; then
echo "ERROR: missing the name of the Quagga daemon to boot"
exit 1
elif [ "$1" = "vtysh" ]; then
bootvtysh $1
else
bootdaemon $1
fi
""" % (cls._configs[0], quagga_sbin_search, quagga_bin_search, \
QUAGGA_STATE_DIR, QUAGGA_USER, QUAGGA_GROUP)
addservice(Zebra)
class QuaggaService(CoreService):
''' Parent class for Quagga services. Defines properties and methods
common to Quagga's routing daemons.
'''
_name = "QuaggaDaemon"
_group = "Quagga"
_depends = ("zebra", )
_dirs = ()
_configs = ()
_startindex = 40
_startup = ()
_shutdown = ()
_meta = "The config file for this service can be found in the Zebra service."
_ipv4_routing = False
_ipv6_routing = False
@staticmethod
def routerid(node):
''' Helper to return the first IPv4 address of a node as its router ID.
'''
for ifc in node.netifs():
if hasattr(ifc, 'control') and ifc.control == True:
continue
for a in ifc.addrlist:
if a.find(".") >= 0:
return a .split('/') [0]
#raise ValueError, "no IPv4 address found for router ID"
return "0.0.0.0"
@staticmethod
def rj45check(ifc):
''' Helper to detect whether interface is connected an external RJ45
link.
'''
if ifc.net:
for peerifc in ifc.net.netifs():
if peerifc == ifc:
continue
if isinstance(peerifc, nodes.RJ45Node):
return True
return False
@classmethod
def generateconfig(cls, node, filename, services):
return ""
@classmethod
def generatequaggaifcconfig(cls, node, ifc):
return ""
@classmethod
def generatequaggaconfig(cls, node):
return ""
class Ospfv2(QuaggaService):
''' The OSPFv2 service provides IPv4 routing for wired networks. It does
not build its own configuration file but has hooks for adding to the
unified Quagga.conf file.
'''
_name = "OSPFv2"
_startup = ("sh quaggaboot.sh ospfd",)
_shutdown = ("killall ospfd", )
_validate = ("pidof ospfd", )
_ipv4_routing = True
@staticmethod
def mtucheck(ifc):
''' Helper to detect MTU mismatch and add the appropriate OSPF
mtu-ignore command. This is needed when e.g. a node is linked via a
GreTap device.
'''
if ifc.mtu != 1500:
# a workaround for PhysicalNode GreTap, which has no knowledge of
# the other nodes/nets
return " ip ospf mtu-ignore\n"
if not ifc.net:
return ""
for i in ifc.net.netifs():
if i.mtu != ifc.mtu:
return " ip ospf mtu-ignore\n"
return ""
@staticmethod
def ptpcheck(ifc):
''' Helper to detect whether interface is connected to a notional
point-to-point link.
'''
if isinstance(ifc.net, nodes.PtpNet):
return " ip ospf network point-to-point\n"
return ""
@classmethod
def generatequaggaconfig(cls, node):
cfg = "router ospf\n"
rtrid = cls.routerid(node)
cfg += " router-id %s\n" % rtrid
# network 10.0.0.0/24 area 0
for ifc in node.netifs():
if hasattr(ifc, 'control') and ifc.control == True:
continue
for a in ifc.addrlist:
if a.find(".") < 0:
continue
net = IPv4Prefix(a)
cfg += " network %s area 0\n" % net
cfg += "!\n"
return cfg
@classmethod
def generatequaggaifcconfig(cls, node, ifc):
return cls.mtucheck(ifc)
#cfg = cls.mtucheck(ifc)
# external RJ45 connections will use default OSPF timers
#if cls.rj45check(ifc):
# return cfg
#cfg += cls.ptpcheck(ifc)
#return cfg + """\
# ip ospf hello-interval 2
# ip ospf dead-interval 6
# ip ospf retransmit-interval 5
#"""
addservice(Ospfv2)
class Ospfv3(QuaggaService):
''' The OSPFv3 service provides IPv6 routing for wired networks. It does
not build its own configuration file but has hooks for adding to the
unified Quagga.conf file.
'''
_name = "OSPFv3"
_startup = ("sh quaggaboot.sh ospf6d",)
_shutdown = ("killall ospf6d", )
_validate = ("pidof ospf6d", )
_ipv4_routing = True
_ipv6_routing = True
@staticmethod
def minmtu(ifc):
''' Helper to discover the minimum MTU of interfaces linked with the
given interface.
'''
mtu = ifc.mtu
if not ifc.net:
return mtu
for i in ifc.net.netifs():
if i.mtu < mtu:
mtu = i.mtu
return mtu
@classmethod
def mtucheck(cls, ifc):
''' Helper to detect MTU mismatch and add the appropriate OSPFv3
ifmtu command. This is needed when e.g. a node is linked via a
GreTap device.
'''
minmtu = cls.minmtu(ifc)
if minmtu < ifc.mtu:
return " ipv6 ospf6 ifmtu %d\n" % minmtu
else:
return ""
@staticmethod
def ptpcheck(ifc):
''' Helper to detect whether interface is connected to a notional
point-to-point link.
'''
if isinstance(ifc.net, nodes.PtpNet):
return " ipv6 ospf6 network point-to-point\n"
return ""
@classmethod
def generatequaggaconfig(cls, node):
cfg = "router ospf6\n"
rtrid = cls.routerid(node)
cfg += " router-id %s\n" % rtrid
for ifc in node.netifs():
if hasattr(ifc, 'control') and ifc.control == True:
continue
cfg += " interface %s area 0.0.0.0\n" % ifc.name
cfg += "!\n"
return cfg
@classmethod
def generatequaggaifcconfig(cls, node, ifc):
return cls.mtucheck(ifc)
#cfg = cls.mtucheck(ifc)
# external RJ45 connections will use default OSPF timers
#if cls.rj45check(ifc):
# return cfg
#cfg += cls.ptpcheck(ifc)
#return cfg + """\
# ipv6 ospf6 hello-interval 2
# ipv6 ospf6 dead-interval 6
# ipv6 ospf6 retransmit-interval 5
#"""
addservice(Ospfv3)
class Ospfv3mdr(Ospfv3):
''' The OSPFv3 MANET Designated Router (MDR) service provides IPv6
routing for wireless networks. It does not build its own
configuration file but has hooks for adding to the
unified Quagga.conf file.
'''
_name = "OSPFv3MDR"
_ipv4_routing = True
@classmethod
def generatequaggaifcconfig(cls, node, ifc):
cfg = cls.mtucheck(ifc)
return cfg + """\
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 uniconnected
ipv6 ospf6 lsafullness mincostlsa
"""
addservice(Ospfv3mdr)
class Bgp(QuaggaService):
'''' The BGP service provides interdomain routing.
Peers must be manually configured, with a full mesh for those
having the same AS number.
'''
_name = "BGP"
_startup = ("sh quaggaboot.sh bgpd",)
_shutdown = ("killall bgpd", )
_validate = ("pidof bgpd", )
_custom_needed = True
_ipv4_routing = True
_ipv6_routing = True
@classmethod
def generatequaggaconfig(cls, node):
cfg = "!\n! BGP configuration\n!\n"
cfg += "! You should configure the AS number below,\n"
cfg += "! along with this router's peers.\n!\n"
cfg += "router bgp %s\n" % node.objid
rtrid = cls.routerid(node)
cfg += " bgp router-id %s\n" % rtrid
cfg += " redistribute connected\n"
cfg += "! neighbor 1.2.3.4 remote-as 555\n!\n"
return cfg
addservice(Bgp)
class Rip(QuaggaService):
''' The RIP service provides IPv4 routing for wired networks.
'''
_name = "RIP"
_startup = ("sh quaggaboot.sh ripd",)
_shutdown = ("killall ripd", )
_validate = ("pidof ripd", )
_ipv4_routing = True
@classmethod
def generatequaggaconfig(cls, node):
cfg = """\
router rip
redistribute static
redistribute connected
redistribute ospf
network 0.0.0.0/0
!
"""
return cfg
addservice(Rip)
class Ripng(QuaggaService):
''' The RIP NG service provides IPv6 routing for wired networks.
'''
_name = "RIPNG"
_startup = ("sh quaggaboot.sh ripngd",)
_shutdown = ("killall ripngd", )
_validate = ("pidof ripngd", )
_ipv6_routing = True
@classmethod
def generatequaggaconfig(cls, node):
cfg = """\
router ripng
redistribute static
redistribute connected
redistribute ospf6
network ::/0
!
"""
return cfg
addservice(Ripng)
class Babel(QuaggaService):
''' The Babel service provides a loop-avoiding distance-vector routing
protocol for IPv6 and IPv4 with fast convergence properties.
'''
_name = "Babel"
_startup = ("sh quaggaboot.sh babeld",)
_shutdown = ("killall babeld", )
_validate = ("pidof babeld", )
_ipv6_routing = True
@classmethod
def generatequaggaconfig(cls, node):
cfg = "router babel\n"
for ifc in node.netifs():
if hasattr(ifc, 'control') and ifc.control == True:
continue
cfg += " network %s\n" % ifc.name
cfg += " redistribute static\n redistribute connected\n"
return cfg
@classmethod
def generatequaggaifcconfig(cls, node, ifc):
type = "wired"
if ifc.net and ifc.net.linktype == coreapi.CORE_LINK_WIRELESS:
return " babel wireless\n no babel split-horizon\n"
else:
return " babel wired\n babel split-horizon\n"
addservice(Babel)
class Vtysh(CoreService):
''' Simple service to run vtysh -b (boot) after all Quagga daemons have
started.
'''
_name = "vtysh"
_group = "Quagga"
_startindex = 45
_startup = ("sh quaggaboot.sh vtysh",)
_shutdown = ()
@classmethod
def generateconfig(cls, node, filename, services):
return ""
addservice(Vtysh)

View file

@ -0,0 +1,129 @@
#
# CORE - define security services : vpnclient, vpnserver, ipsec and firewall
#
# Copyright (c)2011-2012 the Boeing Company.
# See the LICENSE file included in this distribution.
#
'''
security.py: defines security services (vpnclient, vpnserver, ipsec and
firewall)
'''
import os
from core.service import CoreService, addservice
from core.constants import *
class VPNClient(CoreService):
'''
'''
_name = "VPNClient"
_group = "Security"
_configs = ('vpnclient.sh', )
_startindex = 60
_startup = ('sh vpnclient.sh',)
_shutdown = ("killall openvpn",)
_validate = ("pidof openvpn", )
_custom_needed = True
@classmethod
def generateconfig(cls, node, filename, services):
''' Return the client.conf and vpnclient.sh file contents to
'''
cfg = "#!/bin/sh\n"
cfg += "# custom VPN Client configuration for service (security.py)\n"
fname = "%s/examples/services/sampleVPNClient" % CORE_DATA_DIR
try:
cfg += open(fname, "rb").read()
except e:
print "Error opening VPN client configuration template (%s): %s" % \
(fname, e)
return cfg
# this line is required to add the above class to the list of available services
addservice(VPNClient)
class VPNServer(CoreService):
'''
'''
_name = "VPNServer"
_group = "Security"
_configs = ('vpnserver.sh', )
_startindex = 50
_startup = ('sh vpnserver.sh',)
_shutdown = ("killall openvpn",)
_validate = ("pidof openvpn", )
_custom_needed = True
@classmethod
def generateconfig(cls, node, filename, services):
''' Return the sample server.conf and vpnserver.sh file contents to
GUI for user customization.
'''
cfg = "#!/bin/sh\n"
cfg += "# custom VPN Server Configuration for service (security.py)\n"
fname = "%s/examples/services/sampleVPNServer" % CORE_DATA_DIR
try:
cfg += open(fname, "rb").read()
except e:
print "Error opening VPN server configuration template (%s): %s" % \
(fname, e)
return cfg
addservice(VPNServer)
class IPsec(CoreService):
'''
'''
_name = "IPsec"
_group = "Security"
_configs = ('ipsec.sh', )
_startindex = 60
_startup = ('sh ipsec.sh',)
_shutdown = ("killall racoon",)
_custom_needed = True
@classmethod
def generateconfig(cls, node, filename, services):
''' Return the ipsec.conf and racoon.conf file contents to
GUI for user customization.
'''
cfg = "#!/bin/sh\n"
cfg += "# set up static tunnel mode security assocation for service "
cfg += "(security.py)\n"
fname = "%s/examples/services/sampleIPsec" % CORE_DATA_DIR
try:
cfg += open(fname, "rb").read()
except e:
print "Error opening IPsec configuration template (%s): %s" % \
(fname, e)
return cfg
addservice(IPsec)
class Firewall(CoreService):
'''
'''
_name = "Firewall"
_group = "Security"
_configs = ('firewall.sh', )
_startindex = 20
_startup = ('sh firewall.sh',)
_custom_needed = True
@classmethod
def generateconfig(cls, node, filename, services):
''' Return the firewall rule examples to GUI for user customization.
'''
cfg = "#!/bin/sh\n"
cfg += "# custom node firewall rules for service (security.py)\n"
fname = "%s/examples/services/sampleFirewall" % CORE_DATA_DIR
try:
cfg += open(fname, "rb").read()
except e:
print "Error opening Firewall configuration template (%s): %s" % \
(fname, e)
return cfg
addservice(Firewall)

189
daemon/core/services/ucarp.py Executable file
View file

@ -0,0 +1,189 @@
#
# CORE configuration for UCARP
# Copyright (c) 2012 Jonathan deBoer
# See the LICENSE file included in this distribution.
#
#
# author: Jonathan deBoer <jdccdevel@gmail.com>
#
'''
ucarp.py: defines high-availability IP address controlled by ucarp
'''
import os
from core.service import CoreService, addservice
from core.misc.ipaddr import IPv4Prefix
from core.constants import *
UCARP_ETC="/usr/local/etc/ucarp"
class Ucarp(CoreService):
'''
'''
_name = "ucarp"
_group = "Utility"
_depends = ( )
_dirs = (UCARP_ETC, )
_configs = (UCARP_ETC + "/default.sh", UCARP_ETC + "/default-up.sh", UCARP_ETC + "/default-down.sh", "ucarpboot.sh",)
_startindex = 65
_startup = ("sh ucarpboot.sh",)
_shutdown = ("killall ucarp", )
_validate = ("pidof ucarp", )
@classmethod
def generateconfig(cls, node, filename, services):
''' Return the default file contents
'''
if filename == cls._configs[0]:
return cls.generateUcarpConf(node, services)
elif filename == cls._configs[1]:
return cls.generateVipUp(node, services)
elif filename == cls._configs[2]:
return cls.generateVipDown(node, services)
elif filename == cls._configs[3]:
return cls.generateUcarpBoot(node, services)
else:
raise ValueError
@classmethod
def generateUcarpConf(cls, node, services):
''' Returns configuration file text.
'''
try:
ucarp_bin = node.session.cfg['ucarp_bin']
except KeyError:
ucarp_bin = "/usr/sbin/ucarp"
return """\
#!/bin/sh
# Location of UCARP executable
UCARP_EXEC=%s
# Location of the UCARP config directory
UCARP_CFGDIR=%s
# Logging Facility
FACILITY=daemon
# Instance ID
# Any number from 1 to 255
INSTANCE_ID=1
# Password
# Master and Backup(s) need to be the same
PASSWORD="changeme"
# The failover application address
VIRTUAL_ADDRESS=127.0.0.254
VIRTUAL_NET=8
# Interface for IP Address
INTERFACE=lo
# Maintanence address of the local machine
SOURCE_ADDRESS=127.0.0.1
# The ratio number to be considered before marking the node as dead
DEAD_RATIO=3
# UCARP base, lower number will be preferred master
# set to same to have master stay as long as possible
UCARP_BASE=1
SKEW=0
# UCARP options
# -z run shutdown script on exit
# -P force preferred master
# -n don't run down script at start up when we are backup
# -M use broadcast instead of multicast
# -S ignore interface state
OPTIONS="-z -n -M"
# Send extra parameter to down and up scripts
#XPARAM="-x <enter param here>"
XPARAM="-x ${VIRTUAL_NET}"
# The start and stop scripts
START_SCRIPT=${UCARP_CFGDIR}/default-up.sh
STOP_SCRIPT=${UCARP_CFGDIR}/default-down.sh
# These line should not need to be touched
UCARP_OPTS="$OPTIONS -b $UCARP_BASE -k $SKEW -i $INTERFACE -v $INSTANCE_ID -p $PASSWORD -u $START_SCRIPT -d $STOP_SCRIPT -a $VIRTUAL_ADDRESS -s $SOURCE_ADDRESS -f $FACILITY $XPARAM"
${UCARP_EXEC} -B ${UCARP_OPTS}
""" % (ucarp_bin, UCARP_ETC)
@classmethod
def generateUcarpBoot(cls, node, services):
''' Generate a shell script used to boot the Ucarp daemons.
'''
try:
ucarp_bin = node.session.cfg['ucarp_bin']
except KeyError:
ucarp_bin = "/usr/sbin/ucarp"
return """\
#!/bin/sh
# Location of the UCARP config directory
UCARP_CFGDIR=%s
chmod a+x ${UCARP_CFGDIR}/*.sh
# Start the default ucarp daemon configuration
${UCARP_CFGDIR}/default.sh
""" % (UCARP_ETC)
@classmethod
def generateVipUp(cls, node, services):
''' Generate a shell script used to start the virtual ip
'''
try:
ucarp_bin = node.session.cfg['ucarp_bin']
except KeyError:
ucarp_bin = "/usr/sbin/ucarp"
return """\
#!/bin/bash
# Should be invoked as "default-up.sh <dev> <ip>"
exec 2> /dev/null
IP="${2}"
NET="${3}"
if [ -z "$NET" ]; then
NET="24"
fi
/sbin/ip addr add ${IP}/${NET} dev "$1"
"""
@classmethod
def generateVipDown(cls, node, services):
''' Generate a shell script used to stop the virtual ip
'''
try:
ucarp_bin = node.session.cfg['ucarp_bin']
except KeyError:
ucarp_bin = "/usr/sbin/ucarp"
return """\
#!/bin/bash
# Should be invoked as "default-down.sh <dev> <ip>"
exec 2> /dev/null
IP="${2}"
NET="${3}"
if [ -z "$NET" ]; then
NET="24"
fi
/sbin/ip addr del ${IP}/${NET} dev "$1"
"""
addservice(Ucarp)

View file

@ -0,0 +1,676 @@
#
# CORE
# Copyright (c)2010-2012 the Boeing Company.
# See the LICENSE file included in this distribution.
#
# author: Jeff Ahrenholz <jeffrey.m.ahrenholz@boeing.com>
#
'''
utility.py: defines miscellaneous utility services.
'''
import os
from core.service import CoreService, addservice
from core.misc.ipaddr import IPv4Prefix, IPv6Prefix
from core.misc.utils import *
from core.constants import *
class UtilService(CoreService):
''' Parent class for utility services.
'''
_name = "UtilityProcess"
_group = "Utility"
_depends = ()
_dirs = ()
_configs = ()
_startindex = 80
_startup = ()
_shutdown = ()
@classmethod
def generateconfig(cls, node, filename, services):
return ""
class IPForwardService(UtilService):
_name = "IPForward"
_configs = ("ipforward.sh", )
_startindex = 5
_startup = ("sh ipforward.sh", )
@classmethod
def generateconfig(cls, node, filename, services):
if os.uname()[0] == "Linux":
return cls.generateconfiglinux(node, filename, services)
elif os.uname()[0] == "FreeBSD":
return cls.generateconfigbsd(node, filename, services)
else:
raise Exception, "unknown platform"
@classmethod
def generateconfiglinux(cls, node, filename, services):
cfg = """\
#!/bin/sh
# auto-generated by IPForward service (utility.py)
%s -w net.ipv4.conf.all.forwarding=1
%s -w net.ipv6.conf.all.forwarding=1
%s -w net.ipv4.conf.all.send_redirects=0
%s -w net.ipv4.conf.all.rp_filter=0
%s -w net.ipv4.conf.default.rp_filter=0
""" % (SYSCTL_BIN, SYSCTL_BIN, SYSCTL_BIN, SYSCTL_BIN, SYSCTL_BIN)
for ifc in node.netifs():
name = sysctldevname(ifc.name)
cfg += "%s -w net.ipv4.conf.%s.forwarding=1\n" % (SYSCTL_BIN, name)
cfg += "%s -w net.ipv4.conf.%s.send_redirects=0\n" % \
(SYSCTL_BIN, name)
cfg += "%s -w net.ipv4.conf.%s.rp_filter=0\n" % (SYSCTL_BIN, name)
return cfg
@classmethod
def generateconfigbsd(cls, node, filename, services):
return """\
#!/bin/sh
# auto-generated by IPForward service (utility.py)
%s -w net.inet.ip.forwarding=1
%s -w net.inet6.ip6.forwarding=1
%s -w net.inet.icmp.bmcastecho=1
%s -w net.inet.icmp.icmplim=0
""" % (SYSCTL_BIN, SYSCTL_BIN, SYSCTL_BIN, SYSCTL_BIN)
addservice(IPForwardService)
class DefaultRouteService(UtilService):
_name = "DefaultRoute"
_configs = ("defaultroute.sh",)
_startup = ("sh defaultroute.sh",)
@classmethod
def generateconfig(cls, node, filename, services):
cfg = "#!/bin/sh\n"
cfg += "# auto-generated by DefaultRoute service (utility.py)\n"
for ifc in node.netifs():
if hasattr(ifc, 'control') and ifc.control == True:
continue
cfg += "\n".join(map(cls.addrstr, ifc.addrlist))
cfg += "\n"
return cfg
@staticmethod
def addrstr(x):
if x.find(":") >= 0:
net = IPv6Prefix(x)
fam = "inet6 ::"
else:
net = IPv4Prefix(x)
fam = "inet 0.0.0.0"
if net.maxaddr() == net.minaddr():
return ""
else:
if os.uname()[0] == "Linux":
rtcmd = "ip route add default via"
elif os.uname()[0] == "FreeBSD":
rtcmd = "route add -%s" % fam
else:
raise Exception, "unknown platform"
return "%s %s" % (rtcmd, net.minaddr())
addservice(DefaultRouteService)
class DefaultMulticastRouteService(UtilService):
_name = "DefaultMulticastRoute"
_configs = ("defaultmroute.sh",)
_startup = ("sh defaultmroute.sh",)
@classmethod
def generateconfig(cls, node, filename, services):
cfg = "#!/bin/sh\n"
cfg += "# auto-generated by DefaultMulticastRoute service (utility.py)\n"
cfg += "# the first interface is chosen below; please change it "
cfg += "as needed\n"
for ifc in node.netifs():
if hasattr(ifc, 'control') and ifc.control == True:
continue
if os.uname()[0] == "Linux":
rtcmd = "ip route add 224.0.0.0/4 dev"
elif os.uname()[0] == "FreeBSD":
rtcmd = "route add 224.0.0.0/4 -iface"
else:
raise Exception, "unknown platform"
cfg += "%s %s\n" % (rtcmd, ifc.name)
cfg += "\n"
break
return cfg
addservice(DefaultMulticastRouteService)
class StaticRouteService(UtilService):
_name = "StaticRoute"
_configs = ("staticroute.sh",)
_startup = ("sh staticroute.sh",)
_custom_needed = True
@classmethod
def generateconfig(cls, node, filename, services):
cfg = "#!/bin/sh\n"
cfg += "# auto-generated by StaticRoute service (utility.py)\n#\n"
cfg += "# NOTE: this service must be customized to be of any use\n"
cfg += "# Below are samples that you can uncomment and edit.\n#\n"
for ifc in node.netifs():
if hasattr(ifc, 'control') and ifc.control == True:
continue
cfg += "\n".join(map(cls.routestr, ifc.addrlist))
cfg += "\n"
return cfg
@staticmethod
def routestr(x):
if x.find(":") >= 0:
net = IPv6Prefix(x)
fam = "inet6"
dst = "3ffe:4::/64"
else:
net = IPv4Prefix(x)
fam = "inet"
dst = "10.9.8.0/24"
if net.maxaddr() == net.minaddr():
return ""
else:
if os.uname()[0] == "Linux":
rtcmd = "#/sbin/ip route add %s via" % dst
elif os.uname()[0] == "FreeBSD":
rtcmd = "#/sbin/route add -%s %s" % (fam, dst)
else:
raise Exception, "unknown platform"
return "%s %s" % (rtcmd, net.minaddr())
addservice(StaticRouteService)
class SshService(UtilService):
_name = "SSH"
if os.uname()[0] == "FreeBSD":
_configs = ("startsshd.sh", "sshd_config",)
_dirs = ()
else:
_configs = ("startsshd.sh", "/etc/ssh/sshd_config",)
_dirs = ("/etc/ssh", "/var/run/sshd",)
_startup = ("sh startsshd.sh",)
_shutdown = ("killall sshd",)
_validate = ()
@classmethod
def generateconfig(cls, node, filename, services):
''' Use a startup script for launching sshd in order to wait for host
key generation.
'''
if os.uname()[0] == "FreeBSD":
sshcfgdir = node.nodedir
sshstatedir = node.nodedir
sshlibdir = "/usr/libexec"
else:
sshcfgdir = cls._dirs[0]
sshstatedir = cls._dirs[1]
sshlibdir = "/usr/lib/openssh"
if filename == "startsshd.sh":
return """\
#!/bin/sh
# auto-generated by SSH service (utility.py)
ssh-keygen -q -t rsa -N "" -f %s/ssh_host_rsa_key
chmod 655 %s
# wait until RSA host key has been generated to launch sshd
/usr/sbin/sshd -f %s/sshd_config
""" % (sshcfgdir, sshstatedir, sshcfgdir)
else:
return """\
# auto-generated by SSH service (utility.py)
Port 22
Protocol 2
HostKey %s/ssh_host_rsa_key
UsePrivilegeSeparation yes
PidFile %s/sshd.pid
KeyRegenerationInterval 3600
ServerKeyBits 768
SyslogFacility AUTH
LogLevel INFO
LoginGraceTime 120
PermitRootLogin yes
StrictModes yes
RSAAuthentication yes
PubkeyAuthentication yes
IgnoreRhosts yes
RhostsRSAAuthentication no
HostbasedAuthentication no
PermitEmptyPasswords no
ChallengeResponseAuthentication no
X11Forwarding yes
X11DisplayOffset 10
PrintMotd no
PrintLastLog yes
TCPKeepAlive yes
AcceptEnv LANG LC_*
Subsystem sftp %s/sftp-server
UsePAM yes
UseDNS no
""" % (sshcfgdir, sshstatedir, sshlibdir)
addservice(SshService)
class DhcpService(UtilService):
_name = "DHCP"
_configs = ("/etc/dhcp/dhcpd.conf",)
_dirs = ("/etc/dhcp",)
_startup = ("dhcpd",)
_shutdown = ("killall dhcpd",)
_validate = ("pidof dhcpd",)
@classmethod
def generateconfig(cls, node, filename, services):
''' Generate a dhcpd config file using the network address of
each interface.
'''
cfg = """\
# auto-generated by DHCP service (utility.py)
# NOTE: move these option lines into the desired pool { } block(s) below
#option domain-name "test.com";
#option domain-name-servers 10.0.0.1;
#option routers 10.0.0.1;
log-facility local6;
default-lease-time 600;
max-lease-time 7200;
ddns-update-style none;
"""
for ifc in node.netifs():
if hasattr(ifc, 'control') and ifc.control == True:
continue
cfg += "\n".join(map(cls.subnetentry, ifc.addrlist))
cfg += "\n"
return cfg
@staticmethod
def subnetentry(x):
''' Generate a subnet declaration block given an IPv4 prefix string
for inclusion in the dhcpd3 config file.
'''
if x.find(":") >= 0:
return ""
else:
addr = x.split("/")[0]
net = IPv4Prefix(x)
# divide the address space in half
rangelow = net.addr(net.numaddr() / 2)
rangehigh = net.maxaddr()
return """
subnet %s netmask %s {
pool {
range %s %s;
default-lease-time 600;
option routers %s;
}
}
""" % (net.prefixstr(), net.netmaskstr(), rangelow, rangehigh, addr)
addservice(DhcpService)
class DhcpClientService(UtilService):
''' Use a DHCP client for all interfaces for addressing.
'''
_name = "DHCPClient"
_configs = ("startdhcpclient.sh",)
_startup = ("sh startdhcpclient.sh",)
_shutdown = ("killall dhclient",)
_validate = ("pidof dhclient",)
@classmethod
def generateconfig(cls, node, filename, services):
''' Generate a script to invoke dhclient on all interfaces.
'''
cfg = "#!/bin/sh\n"
cfg += "# auto-generated by DHCPClient service (utility.py)\n"
cfg += "# uncomment this mkdir line and symlink line to enable client-"
cfg += "side DNS\n# resolution based on the DHCP server response.\n"
cfg += "#mkdir -p /var/run/resolvconf/interface\n"
for ifc in node.netifs():
if hasattr(ifc, 'control') and ifc.control == True:
continue
cfg += "#ln -s /var/run/resolvconf/interface/%s.dhclient" % ifc.name
cfg += " /var/run/resolvconf/resolv.conf\n"
cfg += "/sbin/dhclient -nw -pf /var/run/dhclient-%s.pid" % ifc.name
cfg += " -lf /var/run/dhclient-%s.lease %s\n" % (ifc.name, ifc.name)
return cfg
addservice(DhcpClientService)
class FtpService(UtilService):
''' Start a vsftpd server.
'''
_name = "FTP"
_configs = ("vsftpd.conf",)
_dirs = ("/var/run/vsftpd/empty", "/var/ftp",)
_startup = ("vsftpd ./vsftpd.conf",)
_shutdown = ("killall vsftpd",)
_validate = ("pidof vsftpd",)
@classmethod
def generateconfig(cls, node, filename, services):
''' Generate a vsftpd.conf configuration file.
'''
return """\
# vsftpd.conf auto-generated by FTP service (utility.py)
listen=YES
anonymous_enable=YES
local_enable=YES
dirmessage_enable=YES
use_localtime=YES
xferlog_enable=YES
connect_from_port_20=YES
xferlog_file=/var/log/vsftpd.log
ftpd_banner=Welcome to the CORE FTP service
secure_chroot_dir=/var/run/vsftpd/empty
anon_root=/var/ftp
"""
addservice(FtpService)
class HttpService(UtilService):
''' Start an apache server.
'''
_name = "HTTP"
_configs = ("/etc/apache2/apache2.conf", "/etc/apache2/envvars",
"/var/www/index.html",)
_dirs = ("/etc/apache2", "/var/run/apache2", "/var/log/apache2",
"/var/lock/apache2", "/var/www", )
_startup = ("apache2ctl start",)
_shutdown = ("apache2ctl stop",)
_validate = ("pidof apache2",)
@classmethod
def generateconfig(cls, node, filename, services):
''' Generate an apache2.conf configuration file.
'''
if filename == cls._configs[0]:
return cls.generateapache2conf(node, filename, services)
elif filename == cls._configs[1]:
return cls.generateenvvars(node, filename, services)
elif filename == cls._configs[2]:
return cls.generatehtml(node, filename, services)
else:
return ""
@classmethod
def generateapache2conf(cls, node, filename, services):
return """\
# apache2.conf generated by utility.py:HttpService
LockFile ${APACHE_LOCK_DIR}/accept.lock
PidFile ${APACHE_PID_FILE}
Timeout 300
KeepAlive On
MaxKeepAliveRequests 100
KeepAliveTimeout 5
<IfModule mpm_prefork_module>
StartServers 5
MinSpareServers 5
MaxSpareServers 10
MaxClients 150
MaxRequestsPerChild 0
</IfModule>
<IfModule mpm_worker_module>
StartServers 2
MinSpareThreads 25
MaxSpareThreads 75
ThreadLimit 64
ThreadsPerChild 25
MaxClients 150
MaxRequestsPerChild 0
</IfModule>
<IfModule mpm_event_module>
StartServers 2
MinSpareThreads 25
MaxSpareThreads 75
ThreadLimit 64
ThreadsPerChild 25
MaxClients 150
MaxRequestsPerChild 0
</IfModule>
User ${APACHE_RUN_USER}
Group ${APACHE_RUN_GROUP}
AccessFileName .htaccess
<Files ~ "^\.ht">
Order allow,deny
Deny from all
Satisfy all
</Files>
DefaultType None
HostnameLookups Off
ErrorLog ${APACHE_LOG_DIR}/error.log
LogLevel warn
#Include mods-enabled/*.load
#Include mods-enabled/*.conf
LoadModule alias_module /usr/lib/apache2/modules/mod_alias.so
LoadModule auth_basic_module /usr/lib/apache2/modules/mod_auth_basic.so
LoadModule authz_default_module /usr/lib/apache2/modules/mod_authz_default.so
LoadModule authz_host_module /usr/lib/apache2/modules/mod_authz_host.so
LoadModule authz_user_module /usr/lib/apache2/modules/mod_authz_user.so
LoadModule autoindex_module /usr/lib/apache2/modules/mod_autoindex.so
LoadModule dir_module /usr/lib/apache2/modules/mod_dir.so
LoadModule env_module /usr/lib/apache2/modules/mod_env.so
NameVirtualHost *:80
Listen 80
<IfModule mod_ssl.c>
Listen 443
</IfModule>
<IfModule mod_gnutls.c>
Listen 443
</IfModule>
LogFormat "%v:%p %h %l %u %t \\"%r\\" %>s %O \\"%{Referer}i\\" \\"%{User-Agent}i\\"" vhost_combined
LogFormat "%h %l %u %t \\"%r\\" %>s %O \\"%{Referer}i\\" \\"%{User-Agent}i\\"" combined
LogFormat "%h %l %u %t \\"%r\\" %>s %O" common
LogFormat "%{Referer}i -> %U" referer
LogFormat "%{User-agent}i" agent
ServerTokens OS
ServerSignature On
TraceEnable Off
<VirtualHost *:80>
ServerAdmin webmaster@localhost
DocumentRoot /var/www
<Directory />
Options FollowSymLinks
AllowOverride None
</Directory>
<Directory /var/www/>
Options Indexes FollowSymLinks MultiViews
AllowOverride None
Order allow,deny
allow from all
</Directory>
ErrorLog ${APACHE_LOG_DIR}/error.log
LogLevel warn
CustomLog ${APACHE_LOG_DIR}/access.log combined
</VirtualHost>
"""
@classmethod
def generateenvvars(cls, node, filename, services):
return """\
# this file is used by apache2ctl - generated by utility.py:HttpService
# these settings come from a default Ubuntu apache2 installation
export APACHE_RUN_USER=www-data
export APACHE_RUN_GROUP=www-data
export APACHE_PID_FILE=/var/run/apache2.pid
export APACHE_RUN_DIR=/var/run/apache2
export APACHE_LOCK_DIR=/var/lock/apache2
export APACHE_LOG_DIR=/var/log/apache2
export LANG=C
export LANG
"""
@classmethod
def generatehtml(cls, node, filename, services):
body = """\
<!-- generated by utility.py:HttpService -->
<h1>%s web server</h1>
<p>This is the default web page for this server.</p>
<p>The web server software is running but no content has been added, yet.</p>
""" % node.name
for ifc in node.netifs():
if hasattr(ifc, 'control') and ifc.control == True:
continue
body += "<li>%s - %s</li>\n" % (ifc.name, ifc.addrlist)
return "<html><body>%s</body></html>" % body
addservice(HttpService)
class PcapService(UtilService):
''' Pcap service for logging packets.
'''
_name = "pcap"
_configs = ("pcap.sh", )
_dirs = ()
_startindex = 1
_startup = ("sh pcap.sh start",)
_shutdown = ("sh pcap.sh stop",)
_validate = ("pidof tcpdump",)
_meta = "logs network traffic to pcap packet capture files"
@classmethod
def generateconfig(cls, node, filename, services):
''' Generate a startpcap.sh traffic logging script.
'''
cfg = """
#!/bin/sh
# set tcpdump options here (see 'man tcpdump' for help)
# (-s snap length, -C limit pcap file length, -n disable name resolution)
DUMPOPTS="-s 12288 -C 10 -n"
if [ "x$1" = "xstart" ]; then
"""
for ifc in node.netifs():
if hasattr(ifc, 'control') and ifc.control == True:
cfg += '# '
redir = "< /dev/null"
cfg += "tcpdump ${DUMPOPTS} -w %s.%s.pcap -i %s %s &\n" % \
(node.name, ifc.name, ifc.name, redir)
cfg += """
elif [ "x$1" = "xstop" ]; then
mkdir -p ${SESSION_DIR}/pcap
mv *.pcap ${SESSION_DIR}/pcap
fi;
"""
return cfg
addservice(PcapService)
class RadvdService(UtilService):
_name = "radvd"
_configs = ("/etc/radvd/radvd.conf",)
_dirs = ("/etc/radvd",)
_startup = ("radvd -C /etc/radvd/radvd.conf -m logfile -l /var/log/radvd.log",)
_shutdown = ("pkill radvd",)
_validate = ("pidof radvd",)
@classmethod
def generateconfig(cls, node, filename, services):
''' Generate a RADVD router advertisement daemon config file
using the network address of each interface.
'''
cfg = "# auto-generated by RADVD service (utility.py)\n"
for ifc in node.netifs():
if hasattr(ifc, 'control') and ifc.control == True:
continue
prefixes = map(cls.subnetentry, ifc.addrlist)
if len(prefixes) < 1:
continue
cfg += """\
interface %s
{
AdvSendAdvert on;
MinRtrAdvInterval 3;
MaxRtrAdvInterval 10;
AdvDefaultPreference low;
AdvHomeAgentFlag off;
""" % ifc.name
for prefix in prefixes:
if prefix == "":
continue
cfg += """\
prefix %s
{
AdvOnLink on;
AdvAutonomous on;
AdvRouterAddr on;
};
""" % prefix
cfg += "};\n"
return cfg
@staticmethod
def subnetentry(x):
''' Generate a subnet declaration block given an IPv6 prefix string
for inclusion in the RADVD config file.
'''
if x.find(":") >= 0:
net = IPv6Prefix(x)
return str(net)
else:
return ""
addservice(RadvdService)
class AtdService(UtilService):
''' Atd service for scheduling at jobs
'''
_name = "atd"
_configs = ("startatd.sh",)
_dirs = ("/var/spool/cron/atjobs", "/var/spool/cron/atspool")
_startup = ("sh startatd.sh", )
_shutdown = ("pkill atd", )
@classmethod
def generateconfig(cls, node, filename, services):
return """
#!/bin/sh
echo 00001 > /var/spool/cron/atjobs/.SEQ
chown -R daemon /var/spool/cron/*
chmod -R 700 /var/spool/cron/*
atd
"""
addservice(AtdService)
class UserDefinedService(UtilService):
''' Dummy service allowing customization of anything.
'''
_name = "UserDefined"
_startindex = 50
_meta = "Customize this service to do anything upon startup."
addservice(UserDefinedService)

View file

@ -0,0 +1,472 @@
#
# CORE
# Copyright (c)2011-2012 the Boeing Company.
# See the LICENSE file included in this distribution.
#
# author: Jeff Ahrenholz <jeffrey.m.ahrenholz@boeing.com>
#
'''
xorp.py: defines routing services provided by the XORP routing suite.
'''
import os
from core.service import CoreService, addservice
from core.misc.ipaddr import IPv4Prefix
from core.constants import *
class XorpRtrmgr(CoreService):
''' XORP router manager service builds a config.boot file based on other
enabled XORP services, and launches necessary daemons upon startup.
'''
_name = "xorp_rtrmgr"
_group = "XORP"
_depends = ()
_dirs = ("/etc/xorp",)
_configs = ("/etc/xorp/config.boot",)
_startindex = 35
_startup = ("xorp_rtrmgr -d -b %s -l /var/log/%s.log -P /var/run/%s.pid" % (_configs[0], _name, _name),)
_shutdown = ("killall xorp_rtrmgr", )
_validate = ("pidof xorp_rtrmgr", )
@classmethod
def generateconfig(cls, node, filename, services):
''' Returns config.boot configuration file text. Other services that
depend on this will have generatexorpconfig() hooks that are
invoked here. Filename currently ignored.
'''
cfg = "interfaces {\n"
for ifc in node.netifs():
cfg += " interface %s {\n" % ifc.name
cfg += "\tvif %s {\n" % ifc.name
cfg += "".join(map(cls.addrstr, ifc.addrlist))
cfg += cls.lladdrstr(ifc)
cfg += "\t}\n"
cfg += " }\n"
cfg += "}\n\n"
for s in services:
try:
s._depends.index(cls._name)
cfg += s.generatexorpconfig(node)
except ValueError:
pass
return cfg
@staticmethod
def addrstr(x):
''' helper for mapping IP addresses to XORP config statements
'''
try:
(addr, plen) = x.split("/")
except Exception:
raise ValueError, "invalid address"
cfg = "\t address %s {\n" % addr
cfg += "\t\tprefix-length: %s\n" % plen
cfg +="\t }\n"
return cfg
@staticmethod
def lladdrstr(ifc):
''' helper for adding link-local address entries (required by OSPFv3)
'''
cfg = "\t address %s {\n" % ifc.hwaddr.tolinklocal()
cfg += "\t\tprefix-length: 64\n"
cfg += "\t }\n"
return cfg
addservice(XorpRtrmgr)
class XorpService(CoreService):
''' Parent class for XORP services. Defines properties and methods
common to XORP's routing daemons.
'''
_name = "XorpDaemon"
_group = "XORP"
_depends = ("xorp_rtrmgr", )
_dirs = ()
_configs = ()
_startindex = 40
_startup = ()
_shutdown = ()
_meta = "The config file for this service can be found in the xorp_rtrmgr service."
@staticmethod
def fea(forwarding):
''' Helper to add a forwarding engine entry to the config file.
'''
cfg = "fea {\n"
cfg += " %s {\n" % forwarding
cfg += "\tdisable:false\n"
cfg += " }\n"
cfg += "}\n"
return cfg
@staticmethod
def mfea(forwarding, ifcs):
''' Helper to add a multicast forwarding engine entry to the config file.
'''
names = []
for ifc in ifcs:
if hasattr(ifc, 'control') and ifc.control == True:
continue
names.append(ifc.name)
names.append("register_vif")
cfg = "plumbing {\n"
cfg += " %s {\n" % forwarding
for name in names:
cfg += "\tinterface %s {\n" % name
cfg += "\t vif %s {\n" % name
cfg += "\t\tdisable: false\n"
cfg += "\t }\n"
cfg += "\t}\n"
cfg += " }\n"
cfg += "}\n"
return cfg
@staticmethod
def policyexportconnected():
''' Helper to add a policy statement for exporting connected routes.
'''
cfg = "policy {\n"
cfg += " policy-statement export-connected {\n"
cfg += "\tterm 100 {\n"
cfg += "\t from {\n"
cfg += "\t\tprotocol: \"connected\"\n"
cfg += "\t }\n"
cfg += "\t}\n"
cfg += " }\n"
cfg += "}\n"
return cfg
@staticmethod
def routerid(node):
''' Helper to return the first IPv4 address of a node as its router ID.
'''
for ifc in node.netifs():
if hasattr(ifc, 'control') and ifc.control == True:
continue
for a in ifc.addrlist:
if a.find(".") >= 0:
return a.split('/')[0]
#raise ValueError, "no IPv4 address found for router ID"
return "0.0.0.0"
@classmethod
def generateconfig(cls, node, filename, services):
return ""
@classmethod
def generatexorpconfig(cls, node):
return ""
class XorpOspfv2(XorpService):
''' The OSPFv2 service provides IPv4 routing for wired networks. It does
not build its own configuration file but has hooks for adding to the
unified XORP configuration file.
'''
_name = "XORP_OSPFv2"
@classmethod
def generatexorpconfig(cls, node):
cfg = cls.fea("unicast-forwarding4")
rtrid = cls.routerid(node)
cfg += "\nprotocols {\n"
cfg += " ospf4 {\n"
cfg += "\trouter-id: %s\n" % rtrid
cfg += "\tarea 0.0.0.0 {\n"
for ifc in node.netifs():
if hasattr(ifc, 'control') and ifc.control == True:
continue
cfg += "\t interface %s {\n" % ifc.name
cfg += "\t\tvif %s {\n" % ifc.name
for a in ifc.addrlist:
if a.find(".") < 0:
continue
addr = a.split("/")[0]
cfg += "\t\t address %s {\n" % addr
cfg += "\t\t }\n"
cfg += "\t\t}\n"
cfg += "\t }\n"
cfg += "\t}\n"
cfg += " }\n"
cfg += "}\n"
return cfg
addservice(XorpOspfv2)
class XorpOspfv3(XorpService):
''' The OSPFv3 service provides IPv6 routing. It does
not build its own configuration file but has hooks for adding to the
unified XORP configuration file.
'''
_name = "XORP_OSPFv3"
@classmethod
def generatexorpconfig(cls, node):
cfg = cls.fea("unicast-forwarding6")
rtrid = cls.routerid(node)
cfg += "\nprotocols {\n"
cfg += " ospf6 0 { /* Instance ID 0 */\n"
cfg += "\trouter-id: %s\n" % rtrid
cfg += "\tarea 0.0.0.0 {\n"
for ifc in node.netifs():
if hasattr(ifc, 'control') and ifc.control == True:
continue
cfg += "\t interface %s {\n" % ifc.name
cfg += "\t\tvif %s {\n" % ifc.name
cfg += "\t\t}\n"
cfg += "\t }\n"
cfg += "\t}\n"
cfg += " }\n"
cfg += "}\n"
return cfg
addservice(XorpOspfv3)
class XorpBgp(XorpService):
''' IPv4 inter-domain routing. AS numbers and peers must be customized.
'''
_name = "XORP_BGP"
_custom_needed = True
@classmethod
def generatexorpconfig(cls, node):
cfg = "/* This is a sample config that should be customized with\n"
cfg += " appropriate AS numbers and peers */\n"
cfg += cls.fea("unicast-forwarding4")
cfg += cls.policyexportconnected()
rtrid = cls.routerid(node)
cfg += "\nprotocols {\n"
cfg += " bgp {\n"
cfg += "\tbgp-id: %s\n" % rtrid
cfg += "\tlocal-as: 65001 /* change this */\n"
cfg += "\texport: \"export-connected\"\n"
cfg += "\tpeer 10.0.1.1 { /* change this */\n"
cfg += "\t local-ip: 10.0.1.1\n"
cfg += "\t as: 65002\n"
cfg += "\t next-hop: 10.0.0.2\n"
cfg += "\t}\n"
cfg += " }\n"
cfg += "}\n"
return cfg
addservice(XorpBgp)
class XorpRip(XorpService):
''' RIP IPv4 unicast routing.
'''
_name = "XORP_RIP"
@classmethod
def generatexorpconfig(cls, node):
cfg = cls.fea("unicast-forwarding4")
cfg += cls.policyexportconnected()
cfg += "\nprotocols {\n"
cfg += " rip {\n"
cfg += "\texport: \"export-connected\"\n"
for ifc in node.netifs():
if hasattr(ifc, 'control') and ifc.control == True:
continue
cfg += "\tinterface %s {\n" % ifc.name
cfg += "\t vif %s {\n" % ifc.name
for a in ifc.addrlist:
if a.find(".") < 0:
continue
addr = a.split("/")[0]
cfg += "\t\taddress %s {\n" % addr
cfg += "\t\t disable: false\n"
cfg += "\t\t}\n"
cfg += "\t }\n"
cfg += "\t}\n"
cfg += " }\n"
cfg += "}\n"
return cfg
addservice(XorpRip)
class XorpRipng(XorpService):
''' RIP NG IPv6 unicast routing.
'''
_name = "XORP_RIPNG"
@classmethod
def generatexorpconfig(cls, node):
cfg = cls.fea("unicast-forwarding6")
cfg += cls.policyexportconnected()
cfg += "\nprotocols {\n"
cfg += " ripng {\n"
cfg += "\texport: \"export-connected\"\n"
for ifc in node.netifs():
if hasattr(ifc, 'control') and ifc.control == True:
continue
cfg += "\tinterface %s {\n" % ifc.name
cfg += "\t vif %s {\n" % ifc.name
# for a in ifc.addrlist:
# if a.find(":") < 0:
# continue
# addr = a.split("/")[0]
# cfg += "\t\taddress %s {\n" % addr
# cfg += "\t\t disable: false\n"
# cfg += "\t\t}\n"
cfg += "\t\taddress %s {\n" % ifc.hwaddr.tolinklocal()
cfg += "\t\t disable: false\n"
cfg += "\t\t}\n"
cfg += "\t }\n"
cfg += "\t}\n"
cfg += " }\n"
cfg += "}\n"
return cfg
addservice(XorpRipng)
class XorpPimSm4(XorpService):
''' PIM Sparse Mode IPv4 multicast routing.
'''
_name = "XORP_PIMSM4"
@classmethod
def generatexorpconfig(cls, node):
cfg = cls.mfea("mfea4", node.netifs())
cfg += "\nprotocols {\n"
cfg += " igmp {\n"
names = []
for ifc in node.netifs():
if hasattr(ifc, 'control') and ifc.control == True:
continue
names.append(ifc.name)
cfg += "\tinterface %s {\n" % ifc.name
cfg += "\t vif %s {\n" % ifc.name
cfg += "\t\tdisable: false\n"
cfg += "\t }\n"
cfg += "\t}\n"
cfg += " }\n"
cfg += "}\n"
cfg += "\nprotocols {\n"
cfg += " pimsm4 {\n"
names.append("register_vif")
for name in names:
cfg += "\tinterface %s {\n" % name
cfg += "\t vif %s {\n" % name
cfg += "\t\tdr-priority: 1\n"
cfg += "\t }\n"
cfg += "\t}\n"
cfg += "\tbootstrap {\n"
cfg += "\t cand-bsr {\n"
cfg += "\t\tscope-zone 224.0.0.0/4 {\n"
cfg += "\t\t cand-bsr-by-vif-name: \"%s\"\n" % names[0]
cfg += "\t\t}\n"
cfg += "\t }\n"
cfg += "\t cand-rp {\n"
cfg += "\t\tgroup-prefix 224.0.0.0/4 {\n"
cfg += "\t\t cand-rp-by-vif-name: \"%s\"\n" % names[0]
cfg += "\t\t}\n"
cfg += "\t }\n"
cfg += "\t}\n"
cfg += " }\n"
cfg += "}\n"
cfg += "\nprotocols {\n"
cfg += " fib2mrib {\n"
cfg += "\tdisable: false\n"
cfg += " }\n"
cfg += "}\n"
return cfg
addservice(XorpPimSm4)
class XorpPimSm6(XorpService):
''' PIM Sparse Mode IPv6 multicast routing.
'''
_name = "XORP_PIMSM6"
@classmethod
def generatexorpconfig(cls, node):
cfg = cls.mfea("mfea6", node.netifs())
cfg += "\nprotocols {\n"
cfg += " mld {\n"
names = []
for ifc in node.netifs():
if hasattr(ifc, 'control') and ifc.control == True:
continue
names.append(ifc.name)
cfg += "\tinterface %s {\n" % ifc.name
cfg += "\t vif %s {\n" % ifc.name
cfg += "\t\tdisable: false\n"
cfg += "\t }\n"
cfg += "\t}\n"
cfg += " }\n"
cfg += "}\n"
cfg += "\nprotocols {\n"
cfg += " pimsm6 {\n"
names.append("register_vif")
for name in names:
cfg += "\tinterface %s {\n" % name
cfg += "\t vif %s {\n" % name
cfg += "\t\tdr-priority: 1\n"
cfg += "\t }\n"
cfg += "\t}\n"
cfg += "\tbootstrap {\n"
cfg += "\t cand-bsr {\n"
cfg += "\t\tscope-zone ff00::/8 {\n"
cfg += "\t\t cand-bsr-by-vif-name: \"%s\"\n" % names[0]
cfg += "\t\t}\n"
cfg += "\t }\n"
cfg += "\t cand-rp {\n"
cfg += "\t\tgroup-prefix ff00::/8 {\n"
cfg += "\t\t cand-rp-by-vif-name: \"%s\"\n" % names[0]
cfg += "\t\t}\n"
cfg += "\t }\n"
cfg += "\t}\n"
cfg += " }\n"
cfg += "}\n"
cfg += "\nprotocols {\n"
cfg += " fib2mrib {\n"
cfg += "\tdisable: false\n"
cfg += " }\n"
cfg += "}\n"
return cfg
addservice(XorpPimSm6)
class XorpOlsr(XorpService):
''' OLSR IPv4 unicast MANET routing.
'''
_name = "XORP_OLSR"
@classmethod
def generatexorpconfig(cls, node):
cfg = cls.fea("unicast-forwarding4")
rtrid = cls.routerid(node)
cfg += "\nprotocols {\n"
cfg += " olsr4 {\n"
cfg += "\tmain-address: %s\n" % rtrid
for ifc in node.netifs():
if hasattr(ifc, 'control') and ifc.control == True:
continue
cfg += "\tinterface %s {\n" % ifc.name
cfg += "\t vif %s {\n" % ifc.name
for a in ifc.addrlist:
if a.find(".") < 0:
continue
addr = a.split("/")[0]
cfg += "\t\taddress %s {\n" % addr
cfg += "\t\t}\n"
cfg += "\t }\n"
cfg += "\t}\n"
cfg += " }\n"
cfg += "}\n"
return cfg
addservice(XorpOlsr)