summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Makefile.am1
-rwxr-xr-xlib/Vyatta/Keepalived.pm280
2 files changed, 281 insertions, 0 deletions
diff --git a/Makefile.am b/Makefile.am
index 5e374b9..1272591 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -62,6 +62,7 @@ share_perl5_DATA += lib/Vyatta/TypeChecker.pm
share_perl5_DATA += lib/Vyatta/ConfigDOMTree.pm
share_perl5_DATA += lib/Vyatta/ConfigOutput.pm
share_perl5_DATA += lib/Vyatta/ConfigLoad.pm
+share_perl5_DATA += lib/Vyatta/Keepalived.pm
default_DATA = etc/default/vyatta-cfg
diff --git a/lib/Vyatta/Keepalived.pm b/lib/Vyatta/Keepalived.pm
new file mode 100755
index 0000000..96d4fe9
--- /dev/null
+++ b/lib/Vyatta/Keepalived.pm
@@ -0,0 +1,280 @@
+#
+# Module: VyattaKeepalived.pm
+#
+# **** License ****
+# 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.
+#
+# This program is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# General Public License for more details.
+#
+# This code was originally developed by Vyatta, Inc.
+# Portions created by Vyatta are Copyright (C) 2007-2009 Vyatta, Inc.
+# All Rights Reserved.
+#
+# Author: Stig Thormodsrud
+# Date: October 2007
+# Description: Common keepalived definitions/funcitions
+#
+# **** End License ****
+#
+package Vyatta::Keepalived;
+our @EXPORT = qw(get_conf_file get_state_script get_state_file
+ vrrp_log vrrp_get_init_state get_changes_file
+ start_daemon restart_daemon stop_daemon
+ vrrp_get_config);
+use base qw(Exporter);
+
+use Vyatta::Config;
+use POSIX;
+
+use strict;
+use warnings;
+
+my $daemon = '/usr/sbin/keepalived';
+my $keepalived_conf = '/etc/keepalived/keepalived.conf';
+my $sbin_dir = '/opt/vyatta/sbin';
+my $state_transition = "$sbin_dir/vyatta-vrrp-state.pl";
+my $keepalived_pid = '/var/run/keepalived_vrrp.pid';
+my $state_dir = '/var/run/vrrpd';
+my $vrrp_log = "$state_dir/vrrp.log";
+my $changes_file = "$state_dir/changes";
+
+sub vrrp_log {
+ my $timestamp = strftime("%Y%m%d-%H:%M.%S", localtime);
+ open my $fh, '>>', $vrrp_log
+ or die "Can't open $vrrp_log:$!";
+ print $fh "$timestamp: ", @_ , "\n";
+ close $fh;
+}
+
+sub is_running {
+ if (-f $keepalived_pid) {
+ my $pid = `cat $keepalived_pid`;
+ $pid =~ s/\s+$//; # chomp doesn't remove nl
+ my $ps = `ps -p $pid -o comm=`;
+
+ if (defined($ps) && $ps ne "") {
+ return 1;
+ }
+ }
+ return 0;
+}
+
+sub start_daemon {
+ my ($conf) = @_;
+
+ my $cmd = "$daemon --vrrp --log-facility 7 --log-detail --dump-conf";
+ $cmd .= " --use-file $conf --vyatta-workaround";
+ system($cmd);
+ vrrp_log("start_daemon");
+}
+
+sub stop_daemon {
+ if (is_running()) {
+ my $pid = `cat $keepalived_pid`;
+ $pid =~ s/\s+$//; # chomp doesn't remove nl
+ system("kill $pid");
+ vrrp_log("stop_daemon");
+ } else {
+ vrrp_log("stop daemon called while not running");
+ }
+}
+
+sub restart_daemon {
+ my ($conf) = @_;
+
+ if (is_running()) {
+ my $pid = `cat $keepalived_pid`;
+ $pid =~ s/\s+$//; # chomp doesn't remove nl
+ system("kill -1 $pid");
+ vrrp_log("restart_deamon");
+ } else {
+ start_daemon($conf);
+ }
+}
+
+sub get_conf_file {
+ return $keepalived_conf;
+}
+
+sub get_state_script {
+ return $state_transition;
+}
+
+sub get_changes_file {
+ system("mkdir $state_dir") if ! -d $state_dir;
+ return $changes_file;
+}
+
+sub get_state_file {
+ my ($vrrp_intf, $vrrp_group) = @_;
+
+ system("mkdir $state_dir") if ! -d $state_dir;
+ my $file = "$state_dir/vrrpd_" . "$vrrp_intf" . "_" . "$vrrp_group.state";
+ return $file;
+}
+
+sub get_master_file {
+ my ($vrrp_intf, $vrrp_group) = @_;
+
+ my $file = "$state_dir/vrrpd_" . "$vrrp_intf" . "_" . "$vrrp_group.master";
+ return $file;
+}
+
+sub get_state_files {
+ my ($intf, $group) = @_;
+
+ # todo: fix sorting for ethX > 9
+ my @state_files;
+ my $LS;
+ if ($group eq "all") {
+ open($LS,"ls $state_dir |grep '^vrrpd_$intf.*\.state\$' | sort |");
+ } else {
+ my $intf_group = $intf . "_" . $group . ".state";
+ open($LS,
+ "ls $state_dir |grep '^vrrpd_$intf_group\$' | sort |");
+ }
+ @state_files = <$LS>;
+ close($LS);
+ foreach my $i (0 .. $#state_files) {
+ $state_files[$i] = "$state_dir/$state_files[$i]";
+ }
+ chomp @state_files;
+ return @state_files;
+}
+
+sub vrrp_get_config {
+ my ($intf, $group) = @_;
+
+ my $path;
+ my $config = new Vyatta::Config;
+
+ if ($intf =~ m/(eth\d+)\.(\d+)/) {
+ $path = "interfaces ethernet $1 vif $2";
+ } else {
+ $path = "interfaces ethernet $intf";
+ }
+
+ $config->setLevel($path);
+ my $primary_addr = $config->returnOrigValue("address");
+ if (!defined $primary_addr) {
+ $primary_addr = "0.0.0.0";
+ }
+ if ($primary_addr =~ m/(\d+\.\d+\.\d+\.\d+)\/\d+/) {
+ $primary_addr = $1; # strip /mask
+ }
+
+ $config->setLevel("$path vrrp vrrp-group $group");
+ my $source_addr = $config->returnOrigValue("hello-source-address");
+ $primary_addr = $source_addr if defined $source_addr;
+
+ my @vips = $config->returnOrigValues("virtual-address");
+ my $priority = $config->returnOrigValue("priority");
+ if (!defined $priority) {
+ $priority = 1;
+ }
+ my $preempt = $config->returnOrigValue("preempt");
+ if (!defined $preempt) {
+ $preempt = "true";
+ }
+ my $advert_int = $config->returnOrigValue("advertise-interval");
+ if (!defined $advert_int) {
+ $advert_int = 1;
+ }
+ $config->setLevel("$path vrrp vrrp-group $group authentication");
+ my $auth_type = $config->returnOrigValue("type");
+ if (!defined $auth_type) {
+ $auth_type = "none";
+ }
+
+ return ($primary_addr, $priority, $preempt, $advert_int, $auth_type, @vips);
+}
+
+sub snoop_for_master {
+ my ($intf, $group, $vip, $timeout) = @_;
+
+ my ($cap_filt, $dis_filt, $options, $cmd);
+
+ my $file = get_master_file($intf, $group);
+
+ # remove mask if vip has one
+ if ($vip =~ /([\d.]+)\/\d+/) {
+ $vip = $1;
+ }
+
+ #
+ # set up common tshark parameters
+ #
+ $cap_filt = "-f \"host 224.0.0.18";
+ $dis_filt = "-R \"vrrp.virt_rtr_id == $group and vrrp.ip_addr == $vip\"";
+ $options = "-a duration:$timeout -p -i$intf -c1 -T pdml";
+
+ my $auth_type = (vrrp_get_config($intf, $group))[4];
+ if (lc($auth_type) ne "ah") {
+ #
+ # the vrrp group is the 2nd byte in the vrrp header
+ #
+ $cap_filt .= " and proto VRRP and vrrp[1:1] = $group\"";
+ $cmd = "tshark $options $cap_filt $dis_filt";
+ system("$cmd > $file 2> /dev/null");
+ } else {
+ #
+ # if the vrrp group is using AH authentication, then the proto will be
+ # AH (0x33) instead of VRRP (0x70). So try snooping for AH and
+ # look for the vrrp group at byte 45 (ip_header=20, ah=24)
+ #
+ $cap_filt .= " and proto 0x33 and ip[45:1] = $group\"";
+ $cmd = "tshark $options $cap_filt $dis_filt";
+ system("$cmd > $file 2> /dev/null");
+ }
+}
+
+sub vrrp_state_parse {
+ my ($file) = @_;
+
+ $file =~ s/\s+$//; # chomp doesn't remove nl
+ if ( -f $file) {
+ my $line = `cat $file`;
+ chomp $line;
+ my ($start_time, $intf, $group, $state, $ltime) = split(' ', $line);
+ return ($start_time, $intf, $group, $state, $ltime);
+ } else {
+ return undef;
+ }
+}
+
+sub vrrp_get_init_state {
+ my ($intf, $group, $vips, $preempt) = @_;
+
+ my $init_state;
+ if (is_running()) {
+ my @state_files = get_state_files($intf, $group);
+ chomp @state_files;
+ if (scalar(@state_files) > 0) {
+ my ($start_time, $f_intf, $f_group, $state, $ltime) =
+ vrrp_state_parse($state_files[0]);
+ if ($state eq "master") {
+ $init_state = 'MASTER';
+ } else {
+ $init_state = 'BACKUP';
+ }
+ return $init_state;
+ }
+ # fall through to logic below
+ }
+
+ if ($preempt eq "false") {
+ $init_state = 'BACKUP';
+ } else {
+ $init_state = 'MASTER';
+ }
+
+ return $init_state;
+}
+
+1;
+#end of file