Skip to content

Instantly share code, notes, and snippets.

@shun159
Created August 9, 2013 05:52
Show Gist options
  • Save shun159/6191440 to your computer and use it in GitHub Desktop.
Save shun159/6191440 to your computer and use it in GitHub Desktop.
#!/usr/bin/perl -w
#
# Module: vyatta-wanloadbalance.pl
#
# This program is free software; you can redistribute it and/or modify it
# under the terms of the GNU General Public License version 2 as published
# by the Free Software Foundation.
#
# **** End License ****
#
use lib "/opt/vyatta/share/perl5/";
use Vyatta::Config;
use Vyatta::Misc;
use Vyatta::TypeChecker;
use Getopt::Long;
use warnings;
use strict;
use POSIX;
use File::Copy;
sub write_health {
#open conf
my $config = new Vyatta::Config;
my $valid = "false";
if ($config->exists("load-balancing wan disable-source-nat")) {
print FILE_LCK "disable-source-nat\n";
}
if ($config->exists("load-balancing wan enable-local-traffic")) {
print FILE_LCK "enable-local-traffic\n";
}
if ($config->exists("load-balancing wan flush-connections")) {
print FILE_LCK "flush-conntrack\n";
}
my $hook = $config->returnValue("load-balancing wan hook");
if (defined $hook) {
print FILE_LCK "hook \"" . $hook . "\"\n";
}
print FILE_LCK "health {\n";
$config->setLevel("load-balancing wan interface-health");
my @eths = $config->listNodes();
foreach my $ethNode (@eths) {
$config->setLevel("load-balancing wan interface-health");
print FILE_LCK "\tinterface " . $ethNode . " {\n";
my $option = $config->returnValue("$ethNode failure-count");
if (defined $option) {
print FILE_LCK "\t\tfailure-ct " . $option . "\n";
}
$option = $config->returnValue("$ethNode success-count");
if (defined $option) {
print FILE_LCK "\t\tsuccess-ct " . $option . "\n";
}
$option = $config->returnValue("$ethNode nexthop");
if (defined $option) {
print FILE_LCK "\t\tnexthop " . $option . "\n";
$valid = "true";
}
else {
print "ERROR: nexthop must be specified\n";
exit 1;
}
$config->setLevel("load-balancing wan interface-health $ethNode test");
my @rules = $config->listNodes();
foreach my $rule (@rules) {
print FILE_LCK "\t\trule " . $rule . " {\n";
my $test_type = $config->returnValue("$rule type");
if ((defined $test_type) && ($test_type eq "ttl")) {
print FILE_LCK "\t\t\ttype udp {\n";
my $ttl_limit = $config->returnValue("$rule ttl-limit");
if (defined $ttl_limit) {
print FILE_LCK "\t\t\t\tttl $ttl_limit\n";
}
}
elsif (defined($test_type) && ($test_type eq "user-defined")) {
print FILE_LCK "\t\t\ttype user-defined {\n";
my $test_script = $config->returnValue("$rule test-script");
if (defined $test_script) {
print FILE_LCK "\t\t\t\ttest-script $test_script\n";
}
else {
print "ERROR: script must be defined for test-script\n";
exit 1;
}
}
else {
print FILE_LCK "\t\t\ttype ping {\n";
}
my $option = $config->returnValue("$rule target");
if (defined $option) {
print FILE_LCK "\t\t\t\ttarget " . $option . "\n";
}
$option = $config->returnValue("$rule resp-time");
if (defined $option) {
print FILE_LCK "\t\t\t\tresp-time " . $option*1000 . "\n";
}
print FILE_LCK "\t\t\t}\n";
print FILE_LCK "\t\t}\n";
}
print FILE_LCK "\t}\n";
}
print FILE_LCK "}\n\n";
if ($valid eq "false") {
print "WARNING: A valid WAN load-balance configuration requires an interface with a nexthop\n";
}
return $valid;
}
sub write_rules {
my $config = new Vyatta::Config;
my $outbound_defined = "false";
$config->setLevel('load-balancing wan rule');
my @rules = $config->listNodes();
#destination
foreach my $rule (@rules) {
print FILE_LCK "rule " . $rule . " {\n";
my $exclude = "false";
$config->setLevel('load-balancing wan rule');
if ($config->exists("$rule exclude")) {
$exclude = "true";
print FILE_LCK "\texclude\n";
}
if ($config->exists("$rule failover")) {
print FILE_LCK "\tfailover\n";
}
if ($config->exists("$rule per-packet-balancing")) {
print FILE_LCK "\tper-packet-balancing\n";
}
if ($config->exists("$rule failover") && $config->exists("$rule exclude")) {
print "ERROR: failover cannot be configured with exclude\n";
exit 1;
}
if ($config->exists("$rule limit") && $config->exists("$rule exclude")) {
print "ERROR: limit cannot be used with exclude\n";
exit 1;
}
if ($config->exists("$rule limit")) {
print FILE_LCK "\tlimit {\n";
my $limit_burst = $config->returnValue("$rule limit burst");
if (defined $limit_burst) {
print FILE_LCK "\t\tburst " . $limit_burst . "\n";
}
my $limit_rate = $config->returnValue("$rule limit rate");
if (defined $limit_rate) {
print FILE_LCK "\t\trate " . $limit_rate . "\n";
}
my $limit_period = $config->returnValue("$rule limit period");
if (defined $limit_period) {
print FILE_LCK "\t\tperiod " . $limit_period . "\n";
}
my $limit_thresh = $config->returnValue("$rule limit threshold");
if (defined $limit_thresh) {
print FILE_LCK "\t\tthresh " . $limit_thresh . "\n";
}
print FILE_LCK "\t}\n";
}
my $protocol = $config->returnValue("$rule protocol");
if (defined $protocol) {
print FILE_LCK "\tprotocol " . $protocol . "\n";
}
else {
$protocol = "";
}
#destination
print FILE_LCK "\tdestination {\n";
my $daddr = $config->returnValue("$rule destination address");
if (defined $daddr) {
if (Vyatta::TypeChecker::validate_iptables4_addr($daddr) eq "1") {
print FILE_LCK "\t\taddress \"" . $daddr . "\"\n";
}
else {
print "Error in destination address configuration\n";
exit 1;
}
}
my $option = $config->returnValue("$rule destination port");
if (defined $option) {
my $can_use_port;
my $port_str;
my $port_err;
if ($protocol eq "tcp" || $protocol eq "udp") {
$can_use_port = "yes";
}
($port_str, $port_err) = Vyatta::Misc::getPortRuleString($option, $can_use_port, "d", $protocol);
if (defined $port_str) {
print FILE_LCK "\t\tport-ipt \"" . $port_str . "\"\n";
}
else {
print $port_err;
exit 1;
}
}
print FILE_LCK "\t}\n";
#source
$config->setLevel('load-balancing wan rule');
print FILE_LCK "\tsource {\n";
my $saddr = $config->returnValue("$rule source address");
if (defined $saddr) {
if (Vyatta::TypeChecker::validate_iptables4_addr($saddr) eq "1") {
print FILE_LCK "\t\taddress \"" . $saddr . "\"\n";
}
else {
print "Error in source address configuration\n";
exit 1;
}
}
$option = $config->returnValue("$rule source port");
if (defined $option) {
my $can_use_port;
my $port_str;
my $port_err;
if ($protocol eq "tcp" || $protocol eq "udp") {
$can_use_port = "yes";
}
($port_str, $port_err) = Vyatta::Misc::getPortRuleString($option, $can_use_port, "s", $protocol);
if (defined $port_str) {
print FILE_LCK "\t\tport-ipt \"" . $port_str . "\"\n";
}
else {
print $port_err;
exit 1;
}
}
print FILE_LCK "\t}\n";
#inbound-interface
my $inbound = $config->returnValue("$rule inbound-interface");
if (defined $inbound) {
print FILE_LCK "\tinbound-interface " . $inbound . "\n"
}
else {
print "ERROR: inbound-interface must be specified\n";
exit 1;
}
#interface
$config->setLevel("load-balancing wan rule $rule interface");
my @eths = $config->listNodes();
if ($#eths < 0 && $exclude eq "false") {
print "WARNING: rule $rule will be inactive because no (outbound) interfaces have been defined for this rule\n";
}
elsif ($#eths >= 0 && $exclude eq "true") {
print "WARNING: interfaces (outbound) are not used when exclude has been defined for rule $rule\n";
}
foreach my $ethNode (@eths) {
if ($inbound eq $ethNode) {
print "WARNING: inbound interface is the same as the outbound interface\n";
}
if ($exclude ne "true") {
$outbound_defined = "true";
}
print FILE_LCK "\tinterface " . $ethNode . " {\n";
$option = $config->returnValue("$ethNode weight");
if (defined $option) {
print FILE_LCK "\t\tweight " . $option . "\n";
}
print FILE_LCK "\t}\n";
}
print FILE_LCK "}\n";
}
if ($outbound_defined eq "false") {
print "WARNING: At least one rule with an (outbound) interface must be defined for WAN load balancing to be active\n";
#allow this configuration, just generate the warning
return "true";
}
return $outbound_defined;
}
my $nexthop;
sub usage {
exit 1;
}
GetOptions("valid-nexthop=s" => \$nexthop,
) or usage();
####main
my $conf_file = '/var/run/load-balance/wlb.conf';
my $conf_lck_file = '/var/run/load-balance/wlb.conf.lck';
`mkdir -p /var/run/load-balance`;
####are we just validating?
if (defined $nexthop) {
my $rc = Vyatta::TypeChecker::validateType('ipv4', $nexthop, 1);
if (!$rc && $nexthop ne "dhcp") {
exit 1;
}
exit 0;
}
#open file
`touch $conf_file`; #creates file on first access
open(FILE, "<$conf_file") or die "Can't open wlb config file";
open(FILE_LCK, "+>$conf_lck_file") or die "Can't open wlb lock file";
my $success = write_health();
if ($success eq "false") {
exit 1;
}
$success = write_rules();
if ($success eq "false") {
exit 1;
}
close FILE;
close FILE_LCK;
copy ($conf_lck_file,$conf_file);
unlink($conf_lck_file);
#finally kick the process
system "/opt/vyatta/sbin/vyatta-wanloadbalance.init restart $conf_file 2>/dev/null";
exit 0;
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment