2017-04-25 16:45:34 +01:00
|
|
|
"""
|
|
|
|
quagga.py: defines routing services provided by Quagga.
|
|
|
|
"""
|
2013-08-29 15:21:13 +01:00
|
|
|
|
2017-04-25 16:45:34 +01:00
|
|
|
from core import constants
|
|
|
|
from core.enumerations import LinkTypes, NodeTypes
|
|
|
|
from core.misc import ipaddress
|
|
|
|
from core.misc import nodeutils
|
|
|
|
from core.service import CoreService
|
|
|
|
|
|
|
|
|
2013-08-29 15:21:13 +01:00
|
|
|
class Zebra(CoreService):
|
2018-06-15 22:03:27 +01:00
|
|
|
name = "zebra"
|
|
|
|
group = "Quagga"
|
|
|
|
dirs = ("/usr/local/etc/quagga", "/var/run/quagga")
|
|
|
|
configs = (
|
2017-08-02 22:07:56 +01:00
|
|
|
"/usr/local/etc/quagga/Quagga.conf",
|
|
|
|
"quaggaboot.sh",
|
|
|
|
"/usr/local/etc/quagga/vtysh.conf"
|
|
|
|
)
|
2018-06-15 22:03:27 +01:00
|
|
|
startup = ("sh quaggaboot.sh zebra",)
|
|
|
|
shutdown = ("killall zebra",)
|
|
|
|
validate = ("pidof zebra",)
|
2013-08-29 15:21:13 +01:00
|
|
|
|
|
|
|
@classmethod
|
2018-06-22 23:47:02 +01:00
|
|
|
def generate_config(cls, node, filename):
|
2017-04-25 16:45:34 +01:00
|
|
|
"""
|
|
|
|
Return the Quagga.conf or quaggaboot.sh file contents.
|
|
|
|
"""
|
2018-06-15 22:03:27 +01:00
|
|
|
if filename == cls.configs[0]:
|
2018-06-21 22:56:30 +01:00
|
|
|
return cls.generateQuaggaConf(node)
|
2018-06-15 22:03:27 +01:00
|
|
|
elif filename == cls.configs[1]:
|
2018-06-21 22:56:30 +01:00
|
|
|
return cls.generateQuaggaBoot(node)
|
2018-06-15 22:03:27 +01:00
|
|
|
elif filename == cls.configs[2]:
|
2018-06-21 22:56:30 +01:00
|
|
|
return cls.generateVtyshConf(node)
|
2013-08-29 15:21:13 +01:00
|
|
|
else:
|
2018-06-21 22:56:30 +01:00
|
|
|
raise ValueError("file name (%s) is not a known configuration: %s", filename, cls.configs)
|
2017-04-25 16:45:34 +01:00
|
|
|
|
2013-08-29 15:21:13 +01:00
|
|
|
@classmethod
|
2018-06-21 22:56:30 +01:00
|
|
|
def generateVtyshConf(cls, node):
|
2017-04-25 16:45:34 +01:00
|
|
|
"""
|
|
|
|
Returns configuration file text.
|
|
|
|
"""
|
2016-01-06 18:40:34 +00:00
|
|
|
return "service integrated-vtysh-config\n"
|
|
|
|
|
2013-08-29 15:21:13 +01:00
|
|
|
@classmethod
|
2018-06-21 22:56:30 +01:00
|
|
|
def generateQuaggaConf(cls, node):
|
2017-04-25 16:45:34 +01:00
|
|
|
"""
|
2017-06-20 02:09:28 +01:00
|
|
|
Returns configuration file text. Other services that depend on zebra
|
|
|
|
will have generatequaggaifcconfig() and generatequaggaconfig()
|
|
|
|
hooks that are invoked here.
|
|
|
|
"""
|
2013-08-29 15:21:13 +01:00
|
|
|
# we could verify here that filename == Quagga.conf
|
|
|
|
cfg = ""
|
|
|
|
for ifc in node.netifs():
|
2017-07-10 17:25:33 +01:00
|
|
|
cfg += "interface %s\n" % ifc.name
|
2013-08-29 15:21:13 +01:00
|
|
|
# include control interfaces in addressing but not routing daemons
|
2017-07-10 17:25:33 +01:00
|
|
|
if hasattr(ifc, 'control') and ifc.control is True:
|
2013-08-29 15:21:13 +01:00
|
|
|
cfg += " "
|
|
|
|
cfg += "\n ".join(map(cls.addrstr, ifc.addrlist))
|
|
|
|
cfg += "\n"
|
|
|
|
continue
|
|
|
|
cfgv4 = ""
|
|
|
|
cfgv6 = ""
|
|
|
|
want_ipv4 = False
|
|
|
|
want_ipv6 = False
|
2018-06-21 22:56:30 +01:00
|
|
|
for s in node.services:
|
2018-06-22 16:16:59 +01:00
|
|
|
if cls.name not in s.dependencies:
|
2013-08-29 15:21:13 +01:00
|
|
|
continue
|
2017-04-25 16:45:34 +01:00
|
|
|
ifccfg = s.generatequaggaifcconfig(node, ifc)
|
2018-06-15 22:03:27 +01:00
|
|
|
if s.ipv4_routing:
|
2013-08-29 15:21:13 +01:00
|
|
|
want_ipv4 = True
|
2018-06-15 22:03:27 +01:00
|
|
|
if s.ipv6_routing:
|
2013-08-29 15:21:13 +01:00
|
|
|
want_ipv6 = True
|
|
|
|
cfgv6 += ifccfg
|
|
|
|
else:
|
|
|
|
cfgv4 += ifccfg
|
2017-04-25 16:45:34 +01:00
|
|
|
|
2013-08-29 15:21:13 +01:00
|
|
|
if want_ipv4:
|
2017-04-25 16:45:34 +01:00
|
|
|
ipv4list = filter(lambda x: ipaddress.is_ipv4_address(x.split('/')[0]), ifc.addrlist)
|
2013-08-29 15:21:13 +01:00
|
|
|
cfg += " "
|
|
|
|
cfg += "\n ".join(map(cls.addrstr, ipv4list))
|
|
|
|
cfg += "\n"
|
|
|
|
cfg += cfgv4
|
|
|
|
if want_ipv6:
|
2017-04-25 16:45:34 +01:00
|
|
|
ipv6list = filter(lambda x: ipaddress.is_ipv6_address(x.split('/')[0]), ifc.addrlist)
|
2013-08-29 15:21:13 +01:00
|
|
|
cfg += " "
|
|
|
|
cfg += "\n ".join(map(cls.addrstr, ipv6list))
|
|
|
|
cfg += "\n"
|
|
|
|
cfg += cfgv6
|
|
|
|
cfg += "!\n"
|
2017-04-25 16:45:34 +01:00
|
|
|
|
2018-06-21 22:56:30 +01:00
|
|
|
for s in node.services:
|
2018-06-22 16:16:59 +01:00
|
|
|
if cls.name not in s.dependencies:
|
2013-08-29 15:21:13 +01:00
|
|
|
continue
|
2017-07-10 17:25:33 +01:00
|
|
|
cfg += s.generatequaggaconfig(node)
|
2013-08-29 15:21:13 +01:00
|
|
|
return cfg
|
2017-04-25 16:45:34 +01:00
|
|
|
|
2013-08-29 15:21:13 +01:00
|
|
|
@staticmethod
|
|
|
|
def addrstr(x):
|
2017-04-25 16:45:34 +01:00
|
|
|
"""
|
|
|
|
helper for mapping IP addresses to zebra config statements
|
|
|
|
"""
|
2013-08-29 15:21:13 +01:00
|
|
|
if x.find(".") >= 0:
|
|
|
|
return "ip address %s" % x
|
|
|
|
elif x.find(":") >= 0:
|
|
|
|
return "ipv6 address %s" % x
|
|
|
|
else:
|
2017-04-25 16:45:34 +01:00
|
|
|
raise ValueError("invalid address: %s", x)
|
|
|
|
|
2013-08-29 15:21:13 +01:00
|
|
|
@classmethod
|
2018-06-21 22:56:30 +01:00
|
|
|
def generateQuaggaBoot(cls, node):
|
2017-04-25 16:45:34 +01:00
|
|
|
"""
|
|
|
|
Generate a shell script used to boot the Quagga daemons.
|
|
|
|
"""
|
2018-06-12 16:37:39 +01:00
|
|
|
quagga_bin_search = node.session.options.get_config("quagga_bin_search",
|
|
|
|
default='"/usr/local/bin /usr/bin /usr/lib/quagga"')
|
|
|
|
quagga_sbin_search = node.session.options.get_config('quagga_sbin_search',
|
|
|
|
default='"/usr/local/sbin /usr/sbin /usr/lib/quagga"')
|
2013-08-29 15:21:13 +01:00
|
|
|
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
|
|
|
|
|
|
|
|
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
|
|
|
|
}
|
|
|
|
|
|
|
|
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
|
|
|
|
|
2016-02-17 18:33:19 +00:00
|
|
|
flags=""
|
|
|
|
|
|
|
|
if [ "$1" = "xpimd" ] && \\
|
|
|
|
grep -E -q '^[[:space:]]*router[[:space:]]+pim6[[:space:]]*$' $QUAGGA_CONF; then
|
|
|
|
flags="$flags -6"
|
|
|
|
fi
|
|
|
|
|
2017-01-13 14:38:23 +00:00
|
|
|
$QUAGGA_SBIN_DIR/$1 $flags -d
|
daemon: streamline Quagga startup
Currently, all Quagga daemons are started concurrently by their
respective CORE services, using "quaggaboot.sh" generated by the
'zebra' service. However, all routing services depend on 'zebra'
already running, and 'vtysh' depends on ALL other Quagga services
before it can push configuration from the common "Quagga.conf" to
all running daemons (see "waitforvtyfiles()" in "quaggaboot.sh").
The spinwait+timeout based implementation of "waitforvtyfiles()"
may, depending on load, give up too early and fail to configure
all Quagga daemons.
This patch streamlines the way Quagga daemons are started, by
launching them all from the 'zebra' service. The correct sequence
is to first launch the 'zebra' daemon itself, then proceed with
all routing daemons, and finish with a call to "vtysh -b" which
configures all running daemons.
The list of all applicable daemons to launch is obtained using
'grep' from Quagga.conf, in the same way "waitforvtyfiles()" used
to discover which *.vty files to look for in /var/run/quagga/.
The startup command for all services other than 'zebra' becomes
empty, and "quaggaboot.sh" issues a warning on attempts to have
it launch any other daemon.
Signed-off-by: Gabriel Somlo <glsomlo@cert.org>
2017-01-12 21:40:45 +00:00
|
|
|
if [ "$?" != "0" ]; then
|
|
|
|
echo "ERROR: Quagga's '$1' daemon failed to start!:"
|
|
|
|
return 1
|
|
|
|
fi
|
2013-08-29 15:21:13 +01:00
|
|
|
}
|
|
|
|
|
daemon: streamline Quagga startup
Currently, all Quagga daemons are started concurrently by their
respective CORE services, using "quaggaboot.sh" generated by the
'zebra' service. However, all routing services depend on 'zebra'
already running, and 'vtysh' depends on ALL other Quagga services
before it can push configuration from the common "Quagga.conf" to
all running daemons (see "waitforvtyfiles()" in "quaggaboot.sh").
The spinwait+timeout based implementation of "waitforvtyfiles()"
may, depending on load, give up too early and fail to configure
all Quagga daemons.
This patch streamlines the way Quagga daemons are started, by
launching them all from the 'zebra' service. The correct sequence
is to first launch the 'zebra' daemon itself, then proceed with
all routing daemons, and finish with a call to "vtysh -b" which
configures all running daemons.
The list of all applicable daemons to launch is obtained using
'grep' from Quagga.conf, in the same way "waitforvtyfiles()" used
to discover which *.vty files to look for in /var/run/quagga/.
The startup command for all services other than 'zebra' becomes
empty, and "quaggaboot.sh" issues a warning on attempts to have
it launch any other daemon.
Signed-off-by: Gabriel Somlo <glsomlo@cert.org>
2017-01-12 21:40:45 +00:00
|
|
|
bootquagga()
|
2013-08-29 15:21:13 +01:00
|
|
|
{
|
daemon: streamline Quagga startup
Currently, all Quagga daemons are started concurrently by their
respective CORE services, using "quaggaboot.sh" generated by the
'zebra' service. However, all routing services depend on 'zebra'
already running, and 'vtysh' depends on ALL other Quagga services
before it can push configuration from the common "Quagga.conf" to
all running daemons (see "waitforvtyfiles()" in "quaggaboot.sh").
The spinwait+timeout based implementation of "waitforvtyfiles()"
may, depending on load, give up too early and fail to configure
all Quagga daemons.
This patch streamlines the way Quagga daemons are started, by
launching them all from the 'zebra' service. The correct sequence
is to first launch the 'zebra' daemon itself, then proceed with
all routing daemons, and finish with a call to "vtysh -b" which
configures all running daemons.
The list of all applicable daemons to launch is obtained using
'grep' from Quagga.conf, in the same way "waitforvtyfiles()" used
to discover which *.vty files to look for in /var/run/quagga/.
The startup command for all services other than 'zebra' becomes
empty, and "quaggaboot.sh" issues a warning on attempts to have
it launch any other daemon.
Signed-off-by: Gabriel Somlo <glsomlo@cert.org>
2017-01-12 21:40:45 +00:00
|
|
|
QUAGGA_BIN_DIR=$(searchforprog 'vtysh' $QUAGGA_BIN_SEARCH)
|
2013-08-29 15:21:13 +01:00
|
|
|
if [ "z$QUAGGA_BIN_DIR" = "z" ]; then
|
daemon: streamline Quagga startup
Currently, all Quagga daemons are started concurrently by their
respective CORE services, using "quaggaboot.sh" generated by the
'zebra' service. However, all routing services depend on 'zebra'
already running, and 'vtysh' depends on ALL other Quagga services
before it can push configuration from the common "Quagga.conf" to
all running daemons (see "waitforvtyfiles()" in "quaggaboot.sh").
The spinwait+timeout based implementation of "waitforvtyfiles()"
may, depending on load, give up too early and fail to configure
all Quagga daemons.
This patch streamlines the way Quagga daemons are started, by
launching them all from the 'zebra' service. The correct sequence
is to first launch the 'zebra' daemon itself, then proceed with
all routing daemons, and finish with a call to "vtysh -b" which
configures all running daemons.
The list of all applicable daemons to launch is obtained using
'grep' from Quagga.conf, in the same way "waitforvtyfiles()" used
to discover which *.vty files to look for in /var/run/quagga/.
The startup command for all services other than 'zebra' becomes
empty, and "quaggaboot.sh" issues a warning on attempts to have
it launch any other daemon.
Signed-off-by: Gabriel Somlo <glsomlo@cert.org>
2017-01-12 21:40:45 +00:00
|
|
|
echo "ERROR: Quagga's 'vtysh' program not found in search path:"
|
|
|
|
echo " $QUAGGA_BIN_SEARCH"
|
2013-08-29 15:21:13 +01:00
|
|
|
return 1
|
|
|
|
fi
|
|
|
|
|
2017-03-31 18:59:58 +01:00
|
|
|
# fix /var/run/quagga permissions
|
|
|
|
id -u quagga 2>/dev/null >/dev/null
|
|
|
|
if [ "$?" = "0" ]; then
|
|
|
|
chown quagga $QUAGGA_STATE_DIR
|
|
|
|
fi
|
|
|
|
|
daemon: streamline Quagga startup
Currently, all Quagga daemons are started concurrently by their
respective CORE services, using "quaggaboot.sh" generated by the
'zebra' service. However, all routing services depend on 'zebra'
already running, and 'vtysh' depends on ALL other Quagga services
before it can push configuration from the common "Quagga.conf" to
all running daemons (see "waitforvtyfiles()" in "quaggaboot.sh").
The spinwait+timeout based implementation of "waitforvtyfiles()"
may, depending on load, give up too early and fail to configure
all Quagga daemons.
This patch streamlines the way Quagga daemons are started, by
launching them all from the 'zebra' service. The correct sequence
is to first launch the 'zebra' daemon itself, then proceed with
all routing daemons, and finish with a call to "vtysh -b" which
configures all running daemons.
The list of all applicable daemons to launch is obtained using
'grep' from Quagga.conf, in the same way "waitforvtyfiles()" used
to discover which *.vty files to look for in /var/run/quagga/.
The startup command for all services other than 'zebra' becomes
empty, and "quaggaboot.sh" issues a warning on attempts to have
it launch any other daemon.
Signed-off-by: Gabriel Somlo <glsomlo@cert.org>
2017-01-12 21:40:45 +00:00
|
|
|
bootdaemon "zebra"
|
2013-08-29 15:21:13 +01:00
|
|
|
for r in rip ripng ospf6 ospf bgp babel; do
|
|
|
|
if grep -q "^router \<${r}\>" $QUAGGA_CONF; then
|
daemon: streamline Quagga startup
Currently, all Quagga daemons are started concurrently by their
respective CORE services, using "quaggaboot.sh" generated by the
'zebra' service. However, all routing services depend on 'zebra'
already running, and 'vtysh' depends on ALL other Quagga services
before it can push configuration from the common "Quagga.conf" to
all running daemons (see "waitforvtyfiles()" in "quaggaboot.sh").
The spinwait+timeout based implementation of "waitforvtyfiles()"
may, depending on load, give up too early and fail to configure
all Quagga daemons.
This patch streamlines the way Quagga daemons are started, by
launching them all from the 'zebra' service. The correct sequence
is to first launch the 'zebra' daemon itself, then proceed with
all routing daemons, and finish with a call to "vtysh -b" which
configures all running daemons.
The list of all applicable daemons to launch is obtained using
'grep' from Quagga.conf, in the same way "waitforvtyfiles()" used
to discover which *.vty files to look for in /var/run/quagga/.
The startup command for all services other than 'zebra' becomes
empty, and "quaggaboot.sh" issues a warning on attempts to have
it launch any other daemon.
Signed-off-by: Gabriel Somlo <glsomlo@cert.org>
2017-01-12 21:40:45 +00:00
|
|
|
bootdaemon "${r}d"
|
2013-08-29 15:21:13 +01:00
|
|
|
fi
|
|
|
|
done
|
2016-02-17 18:33:19 +00:00
|
|
|
|
|
|
|
if grep -E -q '^[[:space:]]*router[[:space:]]+pim6?[[:space:]]*$' $QUAGGA_CONF; then
|
daemon: streamline Quagga startup
Currently, all Quagga daemons are started concurrently by their
respective CORE services, using "quaggaboot.sh" generated by the
'zebra' service. However, all routing services depend on 'zebra'
already running, and 'vtysh' depends on ALL other Quagga services
before it can push configuration from the common "Quagga.conf" to
all running daemons (see "waitforvtyfiles()" in "quaggaboot.sh").
The spinwait+timeout based implementation of "waitforvtyfiles()"
may, depending on load, give up too early and fail to configure
all Quagga daemons.
This patch streamlines the way Quagga daemons are started, by
launching them all from the 'zebra' service. The correct sequence
is to first launch the 'zebra' daemon itself, then proceed with
all routing daemons, and finish with a call to "vtysh -b" which
configures all running daemons.
The list of all applicable daemons to launch is obtained using
'grep' from Quagga.conf, in the same way "waitforvtyfiles()" used
to discover which *.vty files to look for in /var/run/quagga/.
The startup command for all services other than 'zebra' becomes
empty, and "quaggaboot.sh" issues a warning on attempts to have
it launch any other daemon.
Signed-off-by: Gabriel Somlo <glsomlo@cert.org>
2017-01-12 21:40:45 +00:00
|
|
|
bootdaemon "xpimd"
|
2016-02-17 18:33:19 +00:00
|
|
|
fi
|
|
|
|
|
2013-08-29 15:21:13 +01:00
|
|
|
$QUAGGA_BIN_DIR/vtysh -b
|
|
|
|
}
|
|
|
|
|
daemon: streamline Quagga startup
Currently, all Quagga daemons are started concurrently by their
respective CORE services, using "quaggaboot.sh" generated by the
'zebra' service. However, all routing services depend on 'zebra'
already running, and 'vtysh' depends on ALL other Quagga services
before it can push configuration from the common "Quagga.conf" to
all running daemons (see "waitforvtyfiles()" in "quaggaboot.sh").
The spinwait+timeout based implementation of "waitforvtyfiles()"
may, depending on load, give up too early and fail to configure
all Quagga daemons.
This patch streamlines the way Quagga daemons are started, by
launching them all from the 'zebra' service. The correct sequence
is to first launch the 'zebra' daemon itself, then proceed with
all routing daemons, and finish with a call to "vtysh -b" which
configures all running daemons.
The list of all applicable daemons to launch is obtained using
'grep' from Quagga.conf, in the same way "waitforvtyfiles()" used
to discover which *.vty files to look for in /var/run/quagga/.
The startup command for all services other than 'zebra' becomes
empty, and "quaggaboot.sh" issues a warning on attempts to have
it launch any other daemon.
Signed-off-by: Gabriel Somlo <glsomlo@cert.org>
2017-01-12 21:40:45 +00:00
|
|
|
if [ "$1" != "zebra" ]; then
|
|
|
|
echo "WARNING: '$1': all Quagga daemons are launched by the 'zebra' service!"
|
2013-08-29 15:21:13 +01:00
|
|
|
exit 1
|
|
|
|
fi
|
daemon: streamline Quagga startup
Currently, all Quagga daemons are started concurrently by their
respective CORE services, using "quaggaboot.sh" generated by the
'zebra' service. However, all routing services depend on 'zebra'
already running, and 'vtysh' depends on ALL other Quagga services
before it can push configuration from the common "Quagga.conf" to
all running daemons (see "waitforvtyfiles()" in "quaggaboot.sh").
The spinwait+timeout based implementation of "waitforvtyfiles()"
may, depending on load, give up too early and fail to configure
all Quagga daemons.
This patch streamlines the way Quagga daemons are started, by
launching them all from the 'zebra' service. The correct sequence
is to first launch the 'zebra' daemon itself, then proceed with
all routing daemons, and finish with a call to "vtysh -b" which
configures all running daemons.
The list of all applicable daemons to launch is obtained using
'grep' from Quagga.conf, in the same way "waitforvtyfiles()" used
to discover which *.vty files to look for in /var/run/quagga/.
The startup command for all services other than 'zebra' becomes
empty, and "quaggaboot.sh" issues a warning on attempts to have
it launch any other daemon.
Signed-off-by: Gabriel Somlo <glsomlo@cert.org>
2017-01-12 21:40:45 +00:00
|
|
|
confcheck
|
|
|
|
bootquagga
|
2018-06-15 22:03:27 +01:00
|
|
|
""" % (cls.configs[0], quagga_sbin_search, quagga_bin_search, constants.QUAGGA_STATE_DIR)
|
2013-08-29 15:21:13 +01:00
|
|
|
|
|
|
|
|
|
|
|
class QuaggaService(CoreService):
|
2017-04-25 16:45:34 +01:00
|
|
|
"""
|
|
|
|
Parent class for Quagga services. Defines properties and methods
|
|
|
|
common to Quagga's routing daemons.
|
|
|
|
"""
|
2018-06-15 22:03:27 +01:00
|
|
|
name = None
|
|
|
|
group = "Quagga"
|
2018-06-21 19:20:08 +01:00
|
|
|
dependencies = ("zebra",)
|
2018-06-15 22:03:27 +01:00
|
|
|
dirs = ()
|
|
|
|
configs = ()
|
|
|
|
startup = ()
|
|
|
|
shutdown = ()
|
|
|
|
meta = "The config file for this service can be found in the Zebra service."
|
|
|
|
|
|
|
|
ipv4_routing = False
|
|
|
|
ipv6_routing = False
|
2013-08-29 15:21:13 +01:00
|
|
|
|
|
|
|
@staticmethod
|
|
|
|
def routerid(node):
|
2017-04-25 16:45:34 +01:00
|
|
|
"""
|
|
|
|
Helper to return the first IPv4 address of a node as its router ID.
|
|
|
|
"""
|
2013-08-29 15:21:13 +01:00
|
|
|
for ifc in node.netifs():
|
2018-06-15 22:03:27 +01:00
|
|
|
if hasattr(ifc, 'control') and ifc.control is True:
|
2013-08-29 15:21:13 +01:00
|
|
|
continue
|
|
|
|
for a in ifc.addrlist:
|
|
|
|
if a.find(".") >= 0:
|
2017-04-25 16:45:34 +01:00
|
|
|
return a.split('/')[0]
|
|
|
|
# raise ValueError, "no IPv4 address found for router ID"
|
2013-08-29 15:21:13 +01:00
|
|
|
return "0.0.0.0"
|
2017-04-25 16:45:34 +01:00
|
|
|
|
2013-08-29 15:21:13 +01:00
|
|
|
@staticmethod
|
|
|
|
def rj45check(ifc):
|
2017-04-25 16:45:34 +01:00
|
|
|
"""
|
|
|
|
Helper to detect whether interface is connected an external RJ45
|
2013-08-29 15:21:13 +01:00
|
|
|
link.
|
2017-04-25 16:45:34 +01:00
|
|
|
"""
|
2013-08-29 15:21:13 +01:00
|
|
|
if ifc.net:
|
|
|
|
for peerifc in ifc.net.netifs():
|
|
|
|
if peerifc == ifc:
|
|
|
|
continue
|
2017-04-25 16:45:34 +01:00
|
|
|
if nodeutils.is_node(peerifc, NodeTypes.RJ45):
|
2013-08-29 15:21:13 +01:00
|
|
|
return True
|
|
|
|
return False
|
|
|
|
|
|
|
|
@classmethod
|
2018-06-22 23:47:02 +01:00
|
|
|
def generate_config(cls, node, filename):
|
2013-08-29 15:21:13 +01:00
|
|
|
return ""
|
|
|
|
|
|
|
|
@classmethod
|
2017-04-25 16:45:34 +01:00
|
|
|
def generatequaggaifcconfig(cls, node, ifc):
|
2013-08-29 15:21:13 +01:00
|
|
|
return ""
|
|
|
|
|
|
|
|
@classmethod
|
2017-06-20 02:09:28 +01:00
|
|
|
def generatequaggaconfig(cls, node):
|
2013-08-29 15:21:13 +01:00
|
|
|
return ""
|
|
|
|
|
|
|
|
|
|
|
|
class Ospfv2(QuaggaService):
|
2017-04-25 16:45:34 +01:00
|
|
|
"""
|
|
|
|
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.
|
|
|
|
"""
|
2018-06-15 22:03:27 +01:00
|
|
|
name = "OSPFv2"
|
|
|
|
startup = ()
|
|
|
|
shutdown = ("killall ospfd",)
|
|
|
|
validate = ("pidof ospfd",)
|
|
|
|
ipv4_routing = True
|
2013-08-29 15:21:13 +01:00
|
|
|
|
|
|
|
@staticmethod
|
|
|
|
def mtucheck(ifc):
|
2017-04-25 16:45:34 +01:00
|
|
|
"""
|
|
|
|
Helper to detect MTU mismatch and add the appropriate OSPF
|
2013-08-29 15:21:13 +01:00
|
|
|
mtu-ignore command. This is needed when e.g. a node is linked via a
|
|
|
|
GreTap device.
|
2017-04-25 16:45:34 +01:00
|
|
|
"""
|
2013-08-29 15:21:13 +01:00
|
|
|
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):
|
2017-04-25 16:45:34 +01:00
|
|
|
"""
|
|
|
|
Helper to detect whether interface is connected to a notional
|
2013-08-29 15:21:13 +01:00
|
|
|
point-to-point link.
|
2017-04-25 16:45:34 +01:00
|
|
|
"""
|
|
|
|
if nodeutils.is_node(ifc.net, NodeTypes.PEER_TO_PEER):
|
2013-08-29 15:21:13 +01:00
|
|
|
return " ip ospf network point-to-point\n"
|
|
|
|
return ""
|
|
|
|
|
|
|
|
@classmethod
|
2017-06-20 02:09:28 +01:00
|
|
|
def generatequaggaconfig(cls, node):
|
2013-08-29 15:21:13 +01:00
|
|
|
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():
|
2017-04-25 16:45:34 +01:00
|
|
|
if hasattr(ifc, 'control') and ifc.control is True:
|
2013-08-29 15:21:13 +01:00
|
|
|
continue
|
|
|
|
for a in ifc.addrlist:
|
|
|
|
if a.find(".") < 0:
|
|
|
|
continue
|
2017-04-25 16:45:34 +01:00
|
|
|
net = ipaddress.Ipv4Prefix(a)
|
2013-08-29 15:21:13 +01:00
|
|
|
cfg += " network %s area 0\n" % net
|
2017-04-25 16:45:34 +01:00
|
|
|
cfg += "!\n"
|
2013-08-29 15:21:13 +01:00
|
|
|
return cfg
|
2017-04-25 16:45:34 +01:00
|
|
|
|
2013-08-29 15:21:13 +01:00
|
|
|
@classmethod
|
2017-04-25 16:45:34 +01:00
|
|
|
def generatequaggaifcconfig(cls, node, ifc):
|
2013-08-29 15:21:13 +01:00
|
|
|
return cls.mtucheck(ifc)
|
2017-04-25 16:45:34 +01:00
|
|
|
# cfg = cls.mtucheck(ifc)
|
2013-08-29 15:21:13 +01:00
|
|
|
# external RJ45 connections will use default OSPF timers
|
2017-04-25 16:45:34 +01:00
|
|
|
# if cls.rj45check(ifc):
|
2013-08-29 15:21:13 +01:00
|
|
|
# return cfg
|
2017-04-25 16:45:34 +01:00
|
|
|
# cfg += cls.ptpcheck(ifc)
|
|
|
|
|
|
|
|
# return cfg + """\
|
2013-08-29 15:21:13 +01:00
|
|
|
|
2017-04-25 16:45:34 +01:00
|
|
|
|
|
|
|
# ip ospf hello-interval 2
|
2013-08-29 15:21:13 +01:00
|
|
|
# ip ospf dead-interval 6
|
|
|
|
# ip ospf retransmit-interval 5
|
2017-04-25 16:45:34 +01:00
|
|
|
# """
|
|
|
|
|
2013-08-29 15:21:13 +01:00
|
|
|
|
|
|
|
class Ospfv3(QuaggaService):
|
2017-04-25 16:45:34 +01:00
|
|
|
"""
|
|
|
|
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.
|
|
|
|
"""
|
2018-06-15 22:03:27 +01:00
|
|
|
name = "OSPFv3"
|
|
|
|
startup = ()
|
|
|
|
shutdown = ("killall ospf6d",)
|
|
|
|
validate = ("pidof ospf6d",)
|
|
|
|
ipv4_routing = True
|
|
|
|
ipv6_routing = True
|
2013-08-29 15:21:13 +01:00
|
|
|
|
|
|
|
@staticmethod
|
|
|
|
def minmtu(ifc):
|
2017-04-25 16:45:34 +01:00
|
|
|
"""
|
|
|
|
Helper to discover the minimum MTU of interfaces linked with the
|
2013-08-29 15:21:13 +01:00
|
|
|
given interface.
|
2017-04-25 16:45:34 +01:00
|
|
|
"""
|
2013-08-29 15:21:13 +01:00
|
|
|
mtu = ifc.mtu
|
|
|
|
if not ifc.net:
|
|
|
|
return mtu
|
|
|
|
for i in ifc.net.netifs():
|
|
|
|
if i.mtu < mtu:
|
|
|
|
mtu = i.mtu
|
|
|
|
return mtu
|
2017-04-25 16:45:34 +01:00
|
|
|
|
2013-08-29 15:21:13 +01:00
|
|
|
@classmethod
|
|
|
|
def mtucheck(cls, ifc):
|
2017-04-25 16:45:34 +01:00
|
|
|
"""
|
|
|
|
Helper to detect MTU mismatch and add the appropriate OSPFv3
|
2013-08-29 15:21:13 +01:00
|
|
|
ifmtu command. This is needed when e.g. a node is linked via a
|
|
|
|
GreTap device.
|
2017-04-25 16:45:34 +01:00
|
|
|
"""
|
2013-08-29 15:21:13 +01:00
|
|
|
minmtu = cls.minmtu(ifc)
|
|
|
|
if minmtu < ifc.mtu:
|
|
|
|
return " ipv6 ospf6 ifmtu %d\n" % minmtu
|
|
|
|
else:
|
|
|
|
return ""
|
|
|
|
|
|
|
|
@staticmethod
|
|
|
|
def ptpcheck(ifc):
|
2017-04-25 16:45:34 +01:00
|
|
|
"""
|
|
|
|
Helper to detect whether interface is connected to a notional
|
2013-08-29 15:21:13 +01:00
|
|
|
point-to-point link.
|
2017-04-25 16:45:34 +01:00
|
|
|
"""
|
|
|
|
if nodeutils.is_node(ifc.net, NodeTypes.PEER_TO_PEER):
|
2013-08-29 15:21:13 +01:00
|
|
|
return " ipv6 ospf6 network point-to-point\n"
|
|
|
|
return ""
|
|
|
|
|
|
|
|
@classmethod
|
2017-06-20 02:09:28 +01:00
|
|
|
def generatequaggaconfig(cls, node):
|
2013-08-29 15:21:13 +01:00
|
|
|
cfg = "router ospf6\n"
|
|
|
|
rtrid = cls.routerid(node)
|
|
|
|
cfg += " router-id %s\n" % rtrid
|
|
|
|
for ifc in node.netifs():
|
2017-04-25 16:45:34 +01:00
|
|
|
if hasattr(ifc, 'control') and ifc.control is True:
|
2013-08-29 15:21:13 +01:00
|
|
|
continue
|
|
|
|
cfg += " interface %s area 0.0.0.0\n" % ifc.name
|
|
|
|
cfg += "!\n"
|
|
|
|
return cfg
|
2017-04-25 16:45:34 +01:00
|
|
|
|
2013-08-29 15:21:13 +01:00
|
|
|
@classmethod
|
2017-04-25 16:45:34 +01:00
|
|
|
def generatequaggaifcconfig(cls, node, ifc):
|
2013-08-29 15:21:13 +01:00
|
|
|
return cls.mtucheck(ifc)
|
2017-04-25 16:45:34 +01:00
|
|
|
# cfg = cls.mtucheck(ifc)
|
2013-08-29 15:21:13 +01:00
|
|
|
# external RJ45 connections will use default OSPF timers
|
2017-04-25 16:45:34 +01:00
|
|
|
# if cls.rj45check(ifc):
|
2013-08-29 15:21:13 +01:00
|
|
|
# return cfg
|
2017-04-25 16:45:34 +01:00
|
|
|
# cfg += cls.ptpcheck(ifc)
|
|
|
|
|
|
|
|
# return cfg + """\
|
|
|
|
|
2013-08-29 15:21:13 +01:00
|
|
|
|
2017-04-25 16:45:34 +01:00
|
|
|
# ipv6 ospf6 hello-interval 2
|
2013-08-29 15:21:13 +01:00
|
|
|
# ipv6 ospf6 dead-interval 6
|
|
|
|
# ipv6 ospf6 retransmit-interval 5
|
2017-04-25 16:45:34 +01:00
|
|
|
# """
|
2013-08-29 15:21:13 +01:00
|
|
|
|
|
|
|
|
|
|
|
class Ospfv3mdr(Ospfv3):
|
2017-04-25 16:45:34 +01:00
|
|
|
"""
|
|
|
|
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.
|
|
|
|
"""
|
2018-06-15 22:03:27 +01:00
|
|
|
name = "OSPFv3MDR"
|
|
|
|
ipv4_routing = True
|
2013-08-29 15:21:13 +01:00
|
|
|
|
|
|
|
@classmethod
|
2017-04-25 16:45:34 +01:00
|
|
|
def generatequaggaifcconfig(cls, node, ifc):
|
2013-08-29 15:21:13 +01:00
|
|
|
cfg = cls.mtucheck(ifc)
|
2017-06-08 16:31:50 +01:00
|
|
|
# Uncomment the following line to use Address Family Translation for IPv4
|
2013-12-16 18:52:41 +00:00
|
|
|
cfg += " ipv6 ospf6 instance-id 65\n"
|
2017-04-25 16:45:34 +01:00
|
|
|
if ifc.net is not None and nodeutils.is_node(ifc.net, (NodeTypes.WIRELESS_LAN, NodeTypes.EMANE)):
|
2013-12-16 18:52:41 +00:00
|
|
|
return cfg + """\
|
2013-08-29 15:21:13 +01:00
|
|
|
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
|
|
|
|
"""
|
2013-12-16 18:52:41 +00:00
|
|
|
else:
|
|
|
|
return cfg
|
2013-08-29 15:21:13 +01:00
|
|
|
|
|
|
|
|
|
|
|
class Bgp(QuaggaService):
|
2017-04-25 16:45:34 +01:00
|
|
|
"""
|
|
|
|
The BGP service provides interdomain routing.
|
|
|
|
Peers must be manually configured, with a full mesh for those
|
|
|
|
having the same AS number.
|
|
|
|
"""
|
2018-06-15 22:03:27 +01:00
|
|
|
name = "BGP"
|
|
|
|
startup = ()
|
|
|
|
shutdown = ("killall bgpd",)
|
|
|
|
validate = ("pidof bgpd",)
|
|
|
|
custom_needed = True
|
|
|
|
ipv4_routing = True
|
|
|
|
ipv6_routing = True
|
2013-08-29 15:21:13 +01:00
|
|
|
|
|
|
|
@classmethod
|
2017-06-20 02:09:28 +01:00
|
|
|
def generatequaggaconfig(cls, node):
|
2013-08-29 15:21:13 +01:00
|
|
|
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
|
|
|
|
|
|
|
|
|
|
|
|
class Rip(QuaggaService):
|
2017-04-25 16:45:34 +01:00
|
|
|
"""
|
|
|
|
The RIP service provides IPv4 routing for wired networks.
|
|
|
|
"""
|
2018-06-15 22:03:27 +01:00
|
|
|
name = "RIP"
|
|
|
|
startup = ()
|
|
|
|
shutdown = ("killall ripd",)
|
|
|
|
validate = ("pidof ripd",)
|
|
|
|
ipv4_routing = True
|
2013-08-29 15:21:13 +01:00
|
|
|
|
|
|
|
@classmethod
|
2017-06-20 02:09:28 +01:00
|
|
|
def generatequaggaconfig(cls, node):
|
2013-08-29 15:21:13 +01:00
|
|
|
cfg = """\
|
|
|
|
router rip
|
|
|
|
redistribute static
|
|
|
|
redistribute connected
|
|
|
|
redistribute ospf
|
|
|
|
network 0.0.0.0/0
|
|
|
|
!
|
|
|
|
"""
|
|
|
|
return cfg
|
|
|
|
|
|
|
|
|
|
|
|
class Ripng(QuaggaService):
|
2017-04-25 16:45:34 +01:00
|
|
|
"""
|
|
|
|
The RIP NG service provides IPv6 routing for wired networks.
|
|
|
|
"""
|
2018-06-15 22:03:27 +01:00
|
|
|
name = "RIPNG"
|
|
|
|
startup = ()
|
|
|
|
shutdown = ("killall ripngd",)
|
|
|
|
validate = ("pidof ripngd",)
|
|
|
|
ipv6_routing = True
|
2013-08-29 15:21:13 +01:00
|
|
|
|
|
|
|
@classmethod
|
2017-06-20 02:09:28 +01:00
|
|
|
def generatequaggaconfig(cls, node):
|
2013-08-29 15:21:13 +01:00
|
|
|
cfg = """\
|
|
|
|
router ripng
|
|
|
|
redistribute static
|
|
|
|
redistribute connected
|
|
|
|
redistribute ospf6
|
|
|
|
network ::/0
|
|
|
|
!
|
|
|
|
"""
|
|
|
|
return cfg
|
|
|
|
|
|
|
|
|
|
|
|
class Babel(QuaggaService):
|
2017-04-25 16:45:34 +01:00
|
|
|
"""
|
|
|
|
The Babel service provides a loop-avoiding distance-vector routing
|
2013-08-29 15:21:13 +01:00
|
|
|
protocol for IPv6 and IPv4 with fast convergence properties.
|
2017-04-25 16:45:34 +01:00
|
|
|
"""
|
2018-06-15 22:03:27 +01:00
|
|
|
name = "Babel"
|
|
|
|
startup = ()
|
|
|
|
shutdown = ("killall babeld",)
|
|
|
|
validate = ("pidof babeld",)
|
|
|
|
ipv6_routing = True
|
2013-08-29 15:21:13 +01:00
|
|
|
|
|
|
|
@classmethod
|
2017-06-20 02:09:28 +01:00
|
|
|
def generatequaggaconfig(cls, node):
|
2013-08-29 15:21:13 +01:00
|
|
|
cfg = "router babel\n"
|
|
|
|
for ifc in node.netifs():
|
2017-07-06 23:10:50 +01:00
|
|
|
if hasattr(ifc, "control") and ifc.control is True:
|
2013-08-29 15:21:13 +01:00
|
|
|
continue
|
|
|
|
cfg += " network %s\n" % ifc.name
|
|
|
|
cfg += " redistribute static\n redistribute connected\n"
|
|
|
|
return cfg
|
2017-04-25 16:45:34 +01:00
|
|
|
|
2013-08-29 15:21:13 +01:00
|
|
|
@classmethod
|
2017-04-25 16:45:34 +01:00
|
|
|
def generatequaggaifcconfig(cls, node, ifc):
|
2013-08-29 15:21:13 +01:00
|
|
|
type = "wired"
|
2017-04-25 16:45:34 +01:00
|
|
|
if ifc.net and ifc.net.linktype == LinkTypes.WIRELESS.value:
|
2013-08-29 15:21:13 +01:00
|
|
|
return " babel wireless\n no babel split-horizon\n"
|
|
|
|
else:
|
|
|
|
return " babel wired\n babel split-horizon\n"
|
|
|
|
|
|
|
|
|
2016-02-17 18:33:19 +00:00
|
|
|
class Xpimd(QuaggaService):
|
2017-04-25 16:45:34 +01:00
|
|
|
"""
|
2016-02-17 18:33:19 +00:00
|
|
|
PIM multicast routing based on XORP.
|
2017-04-25 16:45:34 +01:00
|
|
|
"""
|
2018-06-15 22:03:27 +01:00
|
|
|
name = 'Xpimd'
|
|
|
|
startup = ()
|
|
|
|
shutdown = ('killall xpimd',)
|
|
|
|
validate = ('pidof xpimd',)
|
|
|
|
ipv4_routing = True
|
2016-02-17 18:33:19 +00:00
|
|
|
|
|
|
|
@classmethod
|
2017-06-20 02:09:28 +01:00
|
|
|
def generatequaggaconfig(cls, node):
|
2016-02-17 18:33:19 +00:00
|
|
|
ifname = 'eth0'
|
|
|
|
for ifc in node.netifs():
|
|
|
|
if ifc.name != 'lo':
|
|
|
|
ifname = ifc.name
|
|
|
|
break
|
|
|
|
cfg = 'router mfea\n!\n'
|
2016-02-18 22:23:10 +00:00
|
|
|
cfg += 'router igmp\n!\n'
|
2016-02-17 18:33:19 +00:00
|
|
|
cfg += 'router pim\n'
|
|
|
|
cfg += ' !ip pim rp-address 10.0.0.1\n'
|
|
|
|
cfg += ' ip pim bsr-candidate %s\n' % ifname
|
|
|
|
cfg += ' ip pim rp-candidate %s\n' % ifname
|
|
|
|
cfg += ' !ip pim spt-threshold interval 10 bytes 80000\n'
|
|
|
|
return cfg
|
|
|
|
|
|
|
|
@classmethod
|
2017-04-25 16:45:34 +01:00
|
|
|
def generatequaggaifcconfig(cls, node, ifc):
|
2016-02-17 18:33:19 +00:00
|
|
|
return ' ip mfea\n ip igmp\n ip pim\n'
|