-
Notifications
You must be signed in to change notification settings - Fork 604
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Adding OSPF cost-shifting as a CARP failover mechanism for scenarios requiring rapid failover in HA pairs. #557
Conversation
Tweak for new output in status_ospfd.php
$ospfd_conf['carpmode'] = "quaggadisable"; | ||
syslog(LOG_NOTICE, "Quagga: CARP failover mode established as 'quaggadisable'"); | ||
} | ||
else { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Move else to same line of }, it's desired style in pfSense
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Will do.
$ospfd_conf['carpmode'] = "none"; | ||
syslog(LOG_NOTICE, "Quagga: CARP failover mode established as 'none'"); | ||
} | ||
write_config( $write_config_only = true ); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
write_config()
expects a string parameter saying the reason of that change to make it easier for users to identify changes.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
OK - how would this look, something along the lines of "write_config( "Applied upgrade script" )"?
I had pulled the "write_config( $write_config_only = true )" function from another part of the original plugin and wasn't entirely sure how it was to be used or what it was expecting.
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)) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Please break this really long line, it's hard to read. Use 4 spaces to indent multi-line statements
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Will do.
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)) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Break long line
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Will do.
write_quagga_running_config(bgpd); | ||
write_quagga_running_config(ospfd); | ||
write_quagga_running_config(ospf6d); | ||
write_quagga_running_config(zebra); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why did you remove quotes here?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Very good question. My production versions haven't had the quotes, so they must've been added in after I submitted the original code 18 months ago that included the "write_quagga_running_config()" function. I'll do some testing on my end to ensure it doesn't impact functionality.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Aside from the issues already pointed out by @rbgarga I just have one small note.
@@ -78,9 +78,9 @@ 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.conf", "/bin/cat {$pkg_homedir}/ospfd.conf"); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Showing the configuration file and the running configuration are two completely different and useful things. The running config is what was interpreted by quagga, so it may differ from the configuration file, and that can aid in tracking down problems with a configuration. You can add the commands to show the running configuration, but do not remove the commands to show the configuration files.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I understand your reasoning here.
My logic behind making it a pure "show running-config" vs. the file that was loaded when the daemon started is that we wanted to see at a glance if an OSPF cost had been set dynamically by viewing the running configuration directly.
With that in mind, I can do as you suggested and show both.
I've made and tested the requested changes and normalized the multi-line functions as they had been done in different manners over time. |
Two things I wish to note:
When selecting your active/standby OSPF costs, I generally give the standby unit lesser values in each field. For example, on my primary units, I set the OSPF "active" cost to 9 and the "backup" cost to 19000. On the standby unit, I set OSPF "active" cost to 5 and the "backup" cost to 15000. That way, if there is a CARP event, the LSA containing the updated cost values from the standby unit will always be more preferable when it is taking over; if both units had the same "active" cost, then there is a chance that connected routers may attempt equal cost load balancing, which does not play well with Quagga, and is anyway not what you want when you've just experienced a CARP event. Likewise, when you fail to "backup" state, you want your costs to be more extreme on the primary unit so that it takes itself out of the OSPF equation if the secondary unit is taking a bit to get its LSAs out. |
net/pfSense-pkg-Quagga_OSPF/files/usr/local/pkg/quagga_ospfd.inc
Outdated
Show resolved
Hide resolved
log_error("Quagga: No assistant generated config for OSPF, but found raw config for one or more daemon"); | ||
} else { | ||
log_error("Quagga OSPFd: No config data found."); | ||
return; | ||
} | ||
|
||
if (isset($config['installedpackages']['quaggaospfdraw']['config'][0]['ospfd']) | ||
&& !empty($config['installedpackages']['quaggaospfdraw']['config'][0]['ospfd'])) { | ||
&& !empty($config['installedpackages']['quaggaospfdraw']['config'][0]['ospfd'])) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
These should be indented with four spaces
@@ -266,7 +266,7 @@ function quagga_ospfd_install_conf() { | |||
|
|||
/* Make zebra config */ | |||
if (isset($config['installedpackages']['quaggaospfdraw']['config'][0]['zebra']) | |||
&& !empty($config['installedpackages']['quaggaospfdraw']['config'][0]['zebra'])) { | |||
&& !empty($config['installedpackages']['quaggaospfdraw']['config'][0]['zebra'])) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
These should be indented with four spaces
@@ -291,11 +291,10 @@ function quagga_ospfd_install_conf() { | |||
|
|||
/* Make bgpd config, add password and logging */ | |||
if (isset($config['installedpackages']['quaggaospfdraw']['config'][0]['bgpd']) | |||
&& !empty($config['installedpackages']['quaggaospfdraw']['config'][0]['bgpd'])) { | |||
&& !empty($config['installedpackages']['quaggaospfdraw']['config'][0]['bgpd'])) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
These should be indented with four spaces
if ($bgpddelmd5file != "") { | ||
$fd = fopen("{$quagga_config_base}/bgpddelmd5pw.conf", "w"); | ||
fwrite($fd, $bgpddelmd5file); | ||
fclose($fd); | ||
} | ||
} | ||
/* Make ospf6 config */ | ||
/* Make ospf6 config */ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why the extra tab here?
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 = ""; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looks like maybe one tab too many here as well
|
||
$carp_ospfcost_status_check = ""; | ||
if ((isset($ospfd_conf['carpmode']) && $ospfd_conf['carpmode'] == "ospfcost") | ||
&& (isset($ospfd_conf['carpcostvid']) && substr($ospfd_conf['carpcostvid'],0,4) != "none") |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
All of these conditions should be indented with four spaces
net/pfSense-pkg-Quagga_OSPF/files/usr/local/pkg/quagga_ospfd.inc
Outdated
Show resolved
Hide resolved
@@ -514,24 +558,86 @@ function quagga_ospfd_plugin_carp($pluginparams) { | |||
if (is_array($config['installedpackages']['quaggaospfd']['config'])) { | |||
$ospfd_conf = &$config['installedpackages']['quaggaospfd']['config'][0]; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
On 2.4.4 and later, this syntax can be dangerous with PHP 7.2 if the user doesn't have any settings.
Check out init_config_arr() for a good way around this, for example, instead of the is_array()
test, use:
init_config_arr(array('installedpackages', 'quaggaospfd', 'config', 0));
$ospfd_conf = &$config['installedpackages']['quaggaospfd']['config'][0];
Do that any time there is an assignment by reference to a multi-level hash like this.
(I know this wasn't part of your commits, but since you're working on it anyhow...)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I need to think about this a bit more since init_config_arr() did not exist on earlier versions of pfsense, and would immediately break the version we are running. Which production version of pfSense was the first to receive this function? I can setup a conditional to use is_array() in older versions.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
2.4.4, but 2.4.4 is also the first to run PHP 7.2. You can't just do one is_array() test you have to test and create each level below it, or PHP 7.2 will barf if it doesn't exist. 2.4.4 is the current supported branch so it is the priority and all new development must be supported there over anything else.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Based on what I'm observing, Init_config_arr() was only introduced in 2.4.4 (config revision 18.8 based on this site https://www.netgate.com/docs/pfsense/releases/versions-of-pfsense-and-freebsd.html ). I'll include a parameter to check the config revision version and if it's 18.8 or greater, it'll use init_config_arr(). If less, it'll use is_array().
Side note - is relying on $config['version'] a suitable solution to determine the pfSense version in play or is there a better way?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You won't be able to install the new version of the package on versions older than 2.4.4, so a version test is unnecessary in the copy of the package that gets committed.
Aside from that, the best way to check the version is to look at $g['product_version']
and then use one of the version compare functions like version_compare_string()
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Got it. I could see where this particular use of is_array() could cause a problem as it's called during any CARP event, whereas the rest are assuming a config is either being built or has been built. That said, I'll test it with a new install and verify init_config_arr() works as expected in this situation.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I just tested with a new install of 2.4.4, configured a CARP address, and added the existing Quagga version, which relies on is_array(). I added logging to the conditions in question to see what happens. I performed a number of CARP tests and no PHP crashes occurred in the cases where there was no Quagga config and in the case where there was a Quagga config.
In looking at init_config_arr(), I'm trying to understand how it should be applied in this case, since the purpose of the if-else statement is to check if a config actually exists. If we have the init_config_arr() function create an array, then won't it artificially pass the test every time? Or would we replace the if-else with the array initialization and let it go to the subsequent tests?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In that kind of case, the is_array() test may be OK but you still need to initialize the array before assigning by reference. It may be safe in certain cases and not in others, depends on the state of the user configuration. Just something to look out for. We've been fixing a lot of similar cases in the base and packages ever since the switch to PHP 7.2.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is this the optimal place to initialize the array then? Should it be initialized somewhere else besides the CARP function call?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
As long as it gets initialized before assigning by reference, it doesn't matter much. You could initialize first and then change the is_array()
check to an !empty()
check.
net/pfSense-pkg-Quagga_OSPF/files/usr/local/pkg/quagga_ospfd.inc
Outdated
Show resolved
Hide resolved
Replaced tabs with spaces as requested.
Removed debug logging from CARP tracking.
@quadrinary sorry for the long delay on this PR. Could you please rebase your fork so we can review and test it? |
Superseded by #769 |
3.1.0 (2022-05-18) * Introduce basic support for OpenSSL version 3 (#492) * Update regex in grep to be POSIX compliant (#556) * Introduce status reporting tools (#555 & #557) * Display certificates using UTF8 (#551) * Allow certificates to be created with fixed date offset (#550) * Add 'verify' to verify certificate against CA (#549) * Add PKCS#12 alias 'friendlyName' (#544) * Disallow use of '--vars=FILE init-pki' (#566) * Support multiple IP-Addresses in SAN (#564) * Add option '--renew-days=NN', custom renew grace period (#557) * Add 'nopass' option to the 'export-pkcs' functions (#411) * Add support for 'busybox' (#543) * Add option '--tmp-dir=DIR' to declare Temp-dir (Commit f503a22) 3.0.9 (2022-05-17) * Upgrade OpenSSL from 1.1.0j to 1.1.1o (#405, #407) - We are buliding this ourselves now. * Fix --version so it uses EASYRSA_OPENSSL (#416) * Use openssl rand instead of non-POSIX mktemp (#478) * Fix paths with spaces (#443) * Correct OpenSSL version from Homebrew on macOs (#416) * Fix revoking a renewed certificate (Original PR #394) Follow-up commit: ef22701878bb10df567d60f2ac50dce52a82c9ee * Introduce 'show-crl' (d1993892178c5219f4a38d50db3b53d1a972b36c) * Support Windows-Git 'version of bash' (#533) * Disallow use of single quote (') in vars file, Warning (#530) * Creating a CA uses x509-types/ca and COMMON (#526) * Prefer 'PKI/vars' over all other locations (#528) * Introduce 'init-pki soft' option (#197) * Warnings are no longer silenced by --batch (#523) * Improve packaging options (#510) * Update regex for POSIX compliance (#556) * Correct date format for Darwin/BSD (#559)
This is an rebased version of Pull Request #413
Code remains the same.
This latest modification to the Quagga package allows users to select the previously existing CARP mode that outright disables the Quagga service or use a hot/warm configuration wherein routers in a HA pair have their OSPF interface costs shifted according to CARP status, allowing for rapid transition from one router to another. Crosslinked in redmine: