#!/usr/bin/perl
# -----------------------------------------------------------------------------
#  Patches openvpn config and enables IP forwarding on MASTER to
#  grant Avatars access to nodes using their internal IP adresses
# -----------------------------------------------------------------------------
#  Author: Alex Tsybulnik
#  Edited by:
#  QA by:  Christopher C Gettings
#  Copyright: videoNEXT LLC
# -----------------------------------------------------------------------------
use strict;
use warnings;

my $APL = $ENV{APL};
if (not $APL) {
# -------------------- standard environment ----------------------------------------
	$APL = '/opt/sarch';
	open (ECF,"$APL/base/etc/env.conf") || die("Cannot read base/etc/env.conf");
	map {$ENV{$1}=$2 if /^(\w+)=(\S+)/} grep {/^\w+=\S+/} <ECF>;
	close ECF;
	@INC = (@INC, split(/:/, $ENV{PERL5LIB}));
#-----------------------------------------------------------------------------------
}

my $MASTER = "/opt/sarch/var/conf/master/s_master";
my $OVPN_CONF = "/etc/openvpn/server.conf";
my $SYSCTL = "/usr/sbin/sysctl";
my $IPTABLES = "/usr/sbin/iptables";

require Master::Conf;
Master::Conf->import(qw(NodeList MasterIP));
require Node::Conf;
Node::Conf->import("ActivationInfo");

my $MasterIP = MasterIP();
my $NodeList = NodeList();
my $ActInfo  = ActivationInfo();

sub patch_openvpn_conf {
	die "OpenVPN config not found!\n" if not -f $OVPN_CONF;
	
	my (@lines, %routes);
	open FH, $OVPN_CONF or die "Cannot open $OVPN_CONF: $!\n";
	@lines = <FH>;
	close FH;
	foreach (@lines) {
		$routes{$1} = 1 if /^push "route ([\d\.]+)"/;
	}
	
	my $restart = 0;
	foreach my $node (values %$NodeList) {
		$routes{$node->{IP}} = 0, $restart = 1 if not $routes{$node->{IP}}; # routes to add
	}
	foreach my $ip (keys %routes) {
		my $found = 0;
		foreach my $node (values %$NodeList) {
			$found = 1, last if $node->{IP} eq $ip;
		}
		$routes{$ip} = 2, $restart = 1 if not $found; # routes to remove
	}
	
	# Now patch openvpn config
	open FH, ">", $OVPN_CONF or die "Cannot open $OVPN_CONF for writing: $!\n";
	foreach (@lines) {
		if (/^push "route ([\d\.]+)"/) {
			next if $routes{$1} == 2;
		}
		print FH $_;
	}
	print FH "push \"route $_\"\n" foreach grep {$routes{$_} == 0} keys %routes;
	close FH;
	
	# Finally, restart OpenVPN daemon if route list was changed
	system("/usr/bin/systemctl restart openvpn\@server.service")
		if $restart;
}

sub setup_forwarding {
	my $node_ips = join ",", map {$_->{IP}} values %$NodeList;
	
	# Enable IP packet forwarding in the kernel
	system("$SYSCTL -w net.ipv4.ip_forward=1 &>/dev/null");
	
	# Add iptables rules
	# First disable forwarding for arbitrary IP packets
	system("$IPTABLES -P FORWARD DROP");
	# Clear existing rules
	system("$IPTABLES -F FORWARD");
	# Enable forwarding for tun0 interface and only to/from nodes
	system("$IPTABLES -A FORWARD -i tun0 -d $node_ips -j ACCEPT");
	system("$IPTABLES -A FORWARD -o tun0 -s $node_ips -j ACCEPT");
}

sub main {
	die "Not a master!\n" if not -f $MASTER;
	
	# Exclude Master from list
	foreach (keys %$NodeList) {
		delete $NodeList->{$_}, last if $NodeList->{$_}{IP} eq $MasterIP;
	}
	
	patch_openvpn_conf if $ActInfo->{ACTIVATED} =~ /v/;
	
	setup_forwarding;
}

main;
