Skip to content

Commit

Permalink
Quagga OSPF cost-shifting as a CARP failover. Issue #8181
Browse files Browse the repository at this point in the history
(cherry picked from commit 1ef0ede)
  • Loading branch information
vktg authored and rbgarga committed Feb 20, 2020
1 parent 9bf3ffb commit 05c9506
Show file tree
Hide file tree
Showing 5 changed files with 209 additions and 23 deletions.
2 changes: 1 addition & 1 deletion net/pfSense-pkg-Quagga_OSPF/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

PORTNAME= pfSense-pkg-Quagga_OSPF
PORTVERSION= 0.6.21
PORTREVISION= 4
PORTREVISION= 5
CATEGORIES= net
MASTER_SITES= # empty
DISTFILES= # empty
Expand Down
22 changes: 22 additions & 0 deletions net/pfSense-pkg-Quagga_OSPF/files/usr/local/bin/quaggactl
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,9 @@ zebra)
bgpr*)
daemon_command ${ZEBRA_PORT} ${ZEBRA_PASSWORD} "show ip route bgp"
;;
run*)
daemon_command ${ZEBRA_PORT} ${ZEBRA_PASSWORD} "enable\nshow running-config "
;;
esac ;;
ospf*)
if [ "`pgrep ospfd`" = "" ]; then
Expand Down Expand Up @@ -87,6 +90,22 @@ ospf*)
rou*)
daemon_command ${OSPF_PORT} ${OSPF_PASSWORD} "show ip ospf route"
;;
run*)
daemon_command ${OSPF_PORT} ${OSPF_PASSWORD} "enable\nshow running-config"
;;
cos*)
if [ -z "$3" ] || [ -z "$4" ]; then
echo "interface or cost not properly specified."
exit 1
fi
case $4 in
0)
daemon_command ${OSPF_PORT} ${OSPF_PASSWORD} "enable\nconfig terminal\ninterface $3\nno ip ospf cost\nend"
;;
*)
daemon_command ${OSPF_PORT} ${OSPF_PASSWORD} "enable\nconfig terminal\ninterface $3\nip ospf cost $4\nend"
;;
esac ;;
esac ;;
bgp6*)
if [ "`pgrep bgpd`" = "" ]; then
Expand Down Expand Up @@ -115,5 +134,8 @@ bgp*)
shift; shift;
daemon_command ${BGP_PORT} ${BGP_PASSWORD} "show ip bgp summary $*"
;;
run*)
daemon_command ${BGP_PORT} ${BGP_PASSWORD} "enable\nshow running-config"
;;
esac ;;
esac
153 changes: 132 additions & 21 deletions net/pfSense-pkg-Quagga_OSPF/files/usr/local/pkg/quagga_ospfd.inc
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,7 @@ function quagga_ospfd_install_conf() {
// generate ospfd.conf based on the assistant
if (is_array($config['installedpackages']['quaggaospfd']['config'])) {
$ospfd_conf = &$config['installedpackages']['quaggaospfd']['config'][0];
} elseif (isset($config['installedpackages']['quaggaospfdraw']['config'][0]['ospfd']) ||
} elseif (isset($config['installedpackages']['quaggaospfdraw']['config'][0]['ospfd']) ||
isset($config['installedpackages']['quaggaospfdraw']['config'][0]['ospf6d']) ||
isset($config['installedpackages']['quaggaospfdraw']['config'][0]['bgpd'])) {
log_error("Quagga: No assistant generated config for OSPF, but found raw config for one or more daemon");
Expand Down Expand Up @@ -293,8 +293,7 @@ function quagga_ospfd_install_conf() {
&& !empty($config['installedpackages']['quaggaospfdraw']['config'][0]['bgpd'])) {
// if there is a raw config specified in the config.xml use that instead of the assisted config
$bgpdconffile = str_replace("\r","",base64_decode($config['installedpackages']['quaggaospfdraw']['config'][0]['bgpd']));
}
else {
} else {
$bgpdconffile = "# This file was created by the pfSense package manager. Do not edit!\n\n";
if ($ospfd_conf['password']) {
$bgpdconffile .= "password {$ospfd_conf['password']}\n";
Expand Down Expand Up @@ -323,39 +322,82 @@ function quagga_ospfd_install_conf() {
fwrite($fd, $bgpdaddmd5file);
fclose($fd);
}

if ($bgpddelmd5file != "") {
$fd = fopen("{$quagga_config_base}/bgpddelmd5pw.conf", "w");
fwrite($fd, $bgpddelmd5file);
fclose($fd);
}
}
/* Make ospf6 config */
/* Make ospf6 config */
if (isset($config['installedpackages']['quaggaospfdraw']['config'][0]['ospf6d'])
&& !empty($config['installedpackages']['quaggaospfdraw']['config'][0]['ospf6d'])) {
// if there is a raw config specified in the config.xml use that instead of the assisted config
$ospf6dconffile = str_replace("\r","",base64_decode($config['installedpackages']['quaggaospfdraw']['config'][0]['ospf6d']));
} else {
$ospf6dconffile = "";
$ospf6dconffile = "";
}

$fd = fopen("{$quagga_config_base}/ospf6d.conf", "w");
fwrite($fd, $ospf6dconffile);
fclose($fd);

//UPGRADE PATH - Checks if legacy "CARP Disable" mode was previously in use proior to 0.6.20; if previously in use, sets "carpmode" to "quaggadisable". If not in use, sets "carpmode" to "none".
if (!isset($ospfd_conf['carpmode'])) {
syslog(LOG_NOTICE, "Quagga: CARP failover mode upgrade initializing");
if (strpos($ospfd_conf['carpstatusvid'], '_vip') !== false) {
$ospfd_conf['carpmode'] = "quaggadisable";
syslog(LOG_NOTICE, "Quagga: CARP failover mode established as 'quaggadisable'");
} else {
$ospfd_conf['carpmode'] = "none";
syslog(LOG_NOTICE, "Quagga: CARP failover mode established as 'none'");
}
write_config( $desc = gettext("Quagga_OSPFd: Upgraded failover method for legacy configurations of Quagga_OSPFD") );
syslog(LOG_NOTICE, "Quagga: CARP failover upgrade complete");
}

$carp_ip_status_check = "";
if (isset($ospfd_conf['carpstatusvid']) && $ospfd_conf['carpstatusvid'] != "none") {
if ((isset($ospfd_conf['carpmode']) && ($ospfd_conf['carpmode'] == "quaggadisable"))
&& (isset($ospfd_conf['carpstatusvid']) && ($ospfd_conf['carpstatusvid'] != "none"))) {
$vip = get_configured_vip($ospfd_conf['carpstatusvid']);
$carpcheckinterface = escapeshellarg(get_real_interface($vip['interface']));
$vhid = escapeshellarg("vhid {$vip['vhid']}");
$carp_ip_status_check = <<<EOF
CARP_STATUS=`/sbin/ifconfig {$carpcheckinterface} | /usr/bin/grep 'carp:' | /usr/bin/grep {$vhid} | /usr/bin/awk '{print \$2;}'`
if [ \${CARP_STATUS} != "MASTER" ]; then
logger "Quagga: CARP \"Quagga Disable\" Mode - Interface {$carpcheckinterface} is NOT in MASTER state, exiting";
exit;
fi
EOF;
}

$carp_ospfcost_status_check = "";
if ((isset($ospfd_conf['carpmode']) && ($ospfd_conf['carpmode'] == "ospfcost"))
&& (isset($ospfd_conf['carpcostvid']) && (substr($ospfd_conf['carpcostvid'], 0, 4) != "none"))
&& (isset($ospfd_conf['carpactivecost']) && isset($ospfd_conf['carpbackupcost']))
&& ($ospfd_conf['carpactivecost'] >= 0 && ($ospfd_conf['carpactivecost'] <= 65535))
&& ($ospfd_conf['carpbackupcost'] >= 0 && ($ospfd_conf['carpbackupcost'] <= 65535))) {
$control_script = "/usr/local/bin/quaggactl";
$carpvips = explode(",", $ospfd_conf['carpcostvid']);
foreach ($carpvips as $vips) {
$vip = get_configured_vip($vips);
$phyint = get_real_interface($vip['interface']);
$carpcheckinterface = escapeshellarg(get_real_interface($vip['interface']));
$vhid = escapeshellarg("vhid {$vip['vhid']}");
$carp_ospfcost_status_check .= <<<EOF
CARP_STATUS=`/sbin/ifconfig {$carpcheckinterface} | /usr/bin/grep 'carp:' | /usr/bin/grep {$vhid} | /usr/bin/awk '{print \$2;}'`
if [ \${CARP_STATUS} == "MASTER" ]; then
{$control_script} ospf cost {$phyint} {$ospfd_conf['carpactivecost']};
logger "Quagga: CARP \"OSPF Cost\" Mode - Interface {$carpcheckinterface} is in MASTER state, OSPF cost set to {$ospfd_conf['carpactivecost']}";
else
{$control_script} ospf cost {$phyint} {$ospfd_conf['carpbackupcost']};
logger "Quagga: CARP \"OSPF Cost\" Mode - Interface {$carpcheckinterface} is NOT in MASTER state, OSPF cost set to {$ospfd_conf['carpbackupcost']}";
fi
EOF;

}
}


// Create rc.d file
Expand Down Expand Up @@ -421,6 +463,7 @@ fi
[ -s {$quagga_config_base}/ospf6d.conf ] && /usr/local/sbin/ospf6d -d -f {$quagga_config_base}/ospf6d.conf
[ -s {$quagga_config_base}/bgpdaddmd5pw.conf ] && /sbin/setkey -f {$quagga_config_base}/bgpdaddmd5pw.conf
[ -s {$quagga_config_base}/bgpd.conf ] && /usr/local/sbin/bgpd -d -f {$quagga_config_base}/bgpd.conf
{$carp_ospfcost_status_check}
EOF;
write_rcfile(array(
Expand All @@ -438,18 +481,21 @@ EOF;
mwexec("/bin/chmod u+rw,go-rw {$quagga_config_base}/bgpd.conf");

// Kick off newly created rc.d script
if (isset($ospfd_conf['carpstatusvid']) && $ospfd_conf['carpstatusvid'] != "none") {
if ((isset($ospfd_conf['carpmode']) && ($ospfd_conf['carpmode'] == "quaggadisable")) &&
(isset($ospfd_conf['carpstatusvid']) && ($ospfd_conf['carpstatusvid'] != "none"))) {
$status = get_carp_interface_status($ospfd_conf['carpstatusvid']);
switch (strtoupper($status)) {
// Stop the service if the VIP is in BACKUP or INIT state.
case "BACKUP":
case "INIT":
mwexec_bg("/usr/local/etc/rc.d/quagga.sh stop");
syslog(LOG_NOTICE, "Quagga: CARP \"Quagga Disable\" Mode - Interface {$carpcheckinterface} is NOT in MASTER state, exiting");
break;
// Start the service if the VIP is MASTER state.
case "MASTER":
// Assume it's up if the status can't be determined.
default:
syslog(LOG_NOTICE, "Quagga: CARP \"Quagga Disable\" Mode - Interface {$carpcheckinterface} is in MASTER state, starting");
mwexec_bg("/usr/local/etc/rc.d/quagga.sh restart");
break;
}
Expand Down Expand Up @@ -515,21 +561,87 @@ function quagga_ospfd_plugin_carp($pluginparams) {
return null;
}
/* If there is no properly configured CARP status check IP, then stop */
if (!isset($ospfd_conf['carpstatusvid']) || $ospfd_conf['carpstatusvid'] == "none") {
if (!isset($ospfd_conf['carpmode']) || ($ospfd_conf['carpmode'] == "none")) {
syslog(LOG_ALERT, "Quagga: CARP failover mode not set.");
return null;
}
list($vhid, $iface) = explode("@", trim($pluginparams['interface']));
$friendly = convert_real_interface_to_friendly_interface_name($iface);
$vip = get_configured_vip($ospfd_conf['carpstatusvid']);
if ($vip['vhid'] != $vhid || $vip['interface'] != $friendly) {
/* Verify "quaggadisable" parameters are properly set.*/
if (($ospfd_conf['carpmode'] == "quaggadisable") && (!isset($ospfd_conf['carpstatusvid']) ||
($ospfd_conf['carpstatusvid'] == "none"))) {
syslog(LOG_ALERT, "Quagga: Failed checking CARP mode parameters: {$ospfd_conf['carpmode']}");
return null;
}
/* Verify "carpmode" parameters are properly set.*/
if (($ospfd_conf['carpmode'] == "ospfcost") && (!isset($ospfd_conf['carpcostvid']) ||
(substr($ospfd_conf['carpcostvid'], 0, 4) == "none")) ||
!isset($ospfd_conf['carpactivecost']) || !isset($ospfd_conf['carpbackupcost']) ||
(($ospfd_conf['carpactivecost'] < 0) || ($ospfd_conf['carpactivecost'] > 65535)) ||
(($ospfd_conf['carpbackupcost'] < 0) || ($ospfd_conf['carpbackupcost'] > 65535))) {
syslog(LOG_ALERT, "Quagga: Failed checking CARP mode parameters: {$ospfd_conf['carpmode']}");
return null;
}

list($vhid, $iface) = explode("@", trim($pluginparams['interface']));
$friendly = convert_real_interface_to_friendly_interface_name($iface);

/* Start or stop the service as needed based on the CARP transition. */
if ($pluginparams['event'] == "rc.carpmaster") {
start_service("Quagga OSPFd");
} elseif ($pluginparams['event'] == "rc.carpbackup") {
stop_service("Quagga OSPFd");
switch ($ospfd_conf['carpmode']) {
case "quaggadisable":
syslog(LOG_INFO, "Quagga: CARP mode {$ospfd_conf['carpmode']}");
$vip = get_configured_vip($ospfd_conf['carpstatusvid']);
syslog(LOG_INFO, "Quagga: CARP VHID and Interface {$vip['vhid']},{$vip['interface']}");
if (($vip['vhid'] != $vhid) || ($vip['interface'] != $friendly)) {
return null;
}

/* Start or stop the service as needed based on the CARP transition. */
if ((isset($ospfd_conf['carpmode']) && ($ospfd_conf['carpmode'] == "quaggadisable")) &&
(isset($ospfd_conf['carpstatusvid']) && ($ospfd_conf['carpstatusvid'] != "none"))) {
if ($pluginparams['event'] == "rc.carpmaster") {
start_service("Quagga OSPFd");
} elseif ($pluginparams['event'] == "rc.carpbackup") {
stop_service("Quagga OSPFd");
}
}
break;
case "ospfcost":
syslog(LOG_INFO, "Quagga: CARP mode {$ospfd_conf['carpmode']}");
$carpvips = explode(",", $ospfd_conf['carpcostvid']);
$match = FALSE;
$i = 0;
foreach ($carpvips as $vip) {
$vips[$i] = get_configured_vip($vip);
if (($vips[$i]['vhid'] == $vhid) && ($vips[$i]['interface'] == $friendly)) {
$match = TRUE;
syslog(LOG_INFO, "Quagga: CARP VHID and Interface {$vips[$i]['vhid']} {$vips[$i]['interface']}");
$phyint = get_real_interface($vips[$i]['interface']);
syslog(LOG_INFO, "Quagga: CARP Interface identified {$phyint}");
}
$i++;
}
unset($vip);

if ($match != TRUE){
syslog(LOG_NOTICE, "Quagga: Couldn't find an Interface or VHID match!");
return null;
}

/* Shift OSPF cost up or downas needed based on the CARP transition. */
$control_script = "/usr/local/bin/quaggactl";
if ((isset($ospfd_conf['carpmode']) && ($ospfd_conf['carpmode'] == "ospfcost")) &&
(isset($ospfd_conf['carpcostvid']) && (substr($ospfd_conf['carpcostvid'], 0, 4) != "none"))) {
if ($pluginparams['event'] == "rc.carpmaster") {
syslog(LOG_INFO, "Quagga: Shifted OSPF Master cost on {$phyint} to {$ospfd_conf['carpactivecost']}");
mwexec("{$control_script} ospf cost {$phyint} {$ospfd_conf['carpactivecost']}");
return null;
} elseif ($pluginparams['event'] == "rc.carpbackup") {
syslog(LOG_INFO, "Quagga: Shifted OSPF Backup cost on {$phyint} to {$ospfd_conf['carpbackupcost']}");
mwexec("{$control_script} ospf cost {$phyint} {$ospfd_conf['carpbackupcost']}");
return null;
}
}
break;
default:
break;
}
}

Expand All @@ -544,11 +656,10 @@ function write_quagga_running_config($module) {
if ( file_exists( $moduleRunningFile ) && ( filesize( $moduleRunningFile ) > 0 ) ) {
$moduleRunning = fopen( "$moduleRunningFile", "r" );
$config['installedpackages']['quaggaospfdraw']['config'][0][$module . "running"] = base64_encode( fread( $moduleRunning, filesize($moduleRunningFile) ));
}
else {
} else {
$config['installedpackages']['quaggaospfdraw']['config'][0][$module . "running"] = base64_encode("!!!!! {$module}.conf does not exist or is empty.");
}
write_config( $write_config_only = true );
write_config( $desc = gettext("Quagga_OSPFd: Wrote {$module}.conf startup-config to pfSense config file."), $backup = false, $write_config_only = true );
}

write_quagga_running_config('bgpd');
Expand Down
52 changes: 51 additions & 1 deletion net/pfSense-pkg-Quagga_OSPF/files/usr/local/pkg/quagga_ospfd.xml
Original file line number Diff line number Diff line change
Expand Up @@ -225,18 +225,68 @@
</rowhelperfield>
</rowhelper>
</field>
<field>
<fielddescr>CARP Mode</fielddescr>
<fieldname>carpmode</fieldname>
<description>
<![CDATA[
Determines OSPF behavior when used in conjunction with CARP.<br /><b>"Quagga Disable"</b> mode keeps Quagga disabled on backup unit until it is active.<br /><b>"OSPF Cost"</b> mode dynamically changes OSPF cost allowing both firwalls to have an active routing table.<br />
]]>
</description>
<type>select</type>
<default_value>none</default_value>
<options>
<option><name>None (Do not track CARP status) (default)</name><value>none</value></option>
<option><name>Quagga Disable (Disable Quagga if backup)</name><value>quaggadisable</value></option>
<option><name>OSPF Cost (Dynamically change OSPF cost)</name><value>ospfcost</value></option>
</options>
</field>
<field>
<fielddescr>CARP Status IP</fielddescr>
<fieldname>carpstatusvid</fieldname>
<description>
<![CDATA[
Used to determine the CARP status. When the CARP vhid is in BACKUP status, quagga will not be started.<br />
<b>For "Quagga Disable" mode.</b> Used to determine the CARP status. When the CARP vhid is in BACKUP status, quagga will not be started.<br />
]]>
</description>
<type>select_source</type>
<source><![CDATA[quagga_ospfd_get_carp_list()]]></source>
<source_name>name</source_name>
<source_value>value</source_value>
</field>
<field>
<fielddescr>OSPF Cost Interfaces</fielddescr>
<fieldname>carpcostvid</fieldname>
<description>
<![CDATA[
<b>For "OSPF Cost" mode.</b> Select interfaces for dynamic OSPF cost changes based on CARP Master/Backup status.<br />
]]>
</description>
<type>select_source</type>
<source><![CDATA[quagga_ospfd_get_carp_list()]]></source>
<source_name>name</source_name>
<source_value>value</source_value>
<multiple></multiple>
</field>
<field>
<fielddescr>CARP OSPF Active Cost</fielddescr>
<fieldname>carpactivecost</fieldname>
<description>
<![CDATA[
<b>For "OSPF Cost" mode.</b> OSPF cost on selected interfaces when CARP Master. (Range 1-65535 or 0 for default/auto cost)<br />
]]>
</description>
<type>input</type>
</field>
<field>
<fielddescr>CARP OSPF Backup Cost</fielddescr>
<fieldname>carpbackupcost</fieldname>
<description>
<![CDATA[
<b>For "OSPF Cost" mode.</b> OSPF cost on selected interfaces when CARP Backup. (Range 1-65535 or 0 for default/auto cost)<br />
]]>
</description>
<type>input</type>
</field>
</fields>
<custom_php_resync_config_command>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -78,8 +78,11 @@ function doCmdT($title, $command) {
defCmdT("Quagga BGP IPv6 Routes", "{$control_script} bgp6 route");
defCmdT("Quagga BGP Neighbors", "{$control_script} bgp neighbor");
defCmdT("Quagga BGP Summary", "{$control_script} bgp sum");
defCmdT("Quagga ospfd running-config", "{$control_script} ospf run");
defCmdT("Quagga ospfd.conf", "/bin/cat {$pkg_homedir}/ospfd.conf");
defCmdT("Quagga bgpd running-config", "{$control_script} bgp run");
defCmdT("Quagga bgpd.conf", "/bin/cat {$pkg_homedir}/bgpd.conf");
defCmdT("Quagga zebra running-config", "{$control_script} zebra run");
defCmdT("Quagga zebra.conf", "/bin/cat {$pkg_homedir}/zebra.conf");

$tab_array = array();
Expand Down

0 comments on commit 05c9506

Please sign in to comment.