diff --git a/contrib/openvpn-status-export.pl b/contrib/openvpn-status-export.pl new file mode 100755 index 0000000..c227a8f --- /dev/null +++ b/contrib/openvpn-status-export.pl @@ -0,0 +1,124 @@ +#!/usr/bin/perl -w +use strict; + +# Script to export the OpenVPN daemon status information (which clients +# are connected from where) as a JSON file that can be served via HTTP. +# +# (C) 2015 by sysmocom - s.f.m.c. GmbH, All rights reserved. +# Author: Harald Welte + +use JSON; +use Linux::Inotify2; +use Net::Netmask; + +my $OPENVPN_STATE_FILE = "/var/tmp/openvpn.status"; +my $JSON_OUTPUT_FILE = "/var/www/openvpn/status.json"; + +my $srcip_table = { + 'Destination 1' => [ + '127.0.0.0/8', + ], + 'Peer 2' => [ + '8.8.0.0/16', '1.2.3.0/18', + ], +}; + +my %netblocks; + +sub read_netmask_table($) +{ + my ($t) = @_; + + foreach my $k (keys %$t) { + my $table = {}; + foreach my $net (@{$$t{$k}}) { + my $block = new Net::Netmask($net); + $block->storeNetblock($table); + } + $netblocks{$k} = $table; + } +} + +sub classify_srcip($) +{ + my ($ip) = @_; + foreach my $k (%netblocks) { + my $block = findNetblock($ip, $netblocks{$k}); + if ($block) { + return $k; + } + } + return undef; +} + +# read the openvpn.status file and parse it, return hash reference to +# its contents. +sub get_openvpn_clients($) +{ + my ($fname) = @_; + my $state = 'init'; + my $href; + my @clients; + + $$href{version} = 1; + + open(INFILE, "<", $fname); + while (my $line = ) { + chomp($line); + if ($line =~ /^OpenVPN CLIENT LIST$/) { + $state = 'client_list'; + } elsif ($line =~ /^ROUTING\ TABLE$/) { + $state = 'routing_table'; + } else { + if ($state eq 'client_list') { + my %cl; + if ($line =~ /^Updated,(.*)/) { + $$href{updated} = $1; + } elsif ($line =~ /^(\S+),([0-9\.]+)\:(\d+),(\d+),(\d+),(.*)$/) { + $cl{name} = $1; + $cl{srcip} = $2; + $cl{operator} = classify_srcip($2); + $cl{srcport} = $3 + 0; + $cl{bytes_rx} = $4 + 0; + $cl{bytes_tx} = $5 + 0; + $cl{connected_since} = $6; + push(@clients, \%cl); + } + } + } + } + close(INFILE); + + $$href{clients} = \@clients; + + return $href; +} + +# inotify handler to re-parse/convert openvpn.status on any change +sub status_in_handler +{ + my $e = shift; + + # read/parse openvpn.status + my $cl = get_openvpn_clients($e->fullname); + + # write result to file + open(OUTFILE, ">", $JSON_OUTPUT_FILE); + print(OUTFILE to_json($cl, { pretty => 1 })); + close(OUTFILE); + + # also print it to console for debugging + print(to_json($cl, { pretty => 1 })); +} + + + +# main + +read_netmask_table($srcip_table); + +my $inotify = new Linux::Inotify2 or die("Can't create inotify object: $!"); +$inotify->watch($OPENVPN_STATE_FILE, IN_MODIFY, \&status_in_handler); + +# endless loop, wait for inotify enents +1 while $inotify->poll;