blob: c227a8f1431e277ebaba405df22ec5d591acec32 [file] [log] [blame]
Max1db6d002018-11-22 16:53:03 +01001#!/usr/bin/perl -w
2use strict;
3
4# Script to export the OpenVPN daemon status information (which clients
5# are connected from where) as a JSON file that can be served via HTTP.
6#
7# (C) 2015 by sysmocom - s.f.m.c. GmbH, All rights reserved.
8# Author: Harald Welte
9
10use JSON;
11use Linux::Inotify2;
12use Net::Netmask;
13
14my $OPENVPN_STATE_FILE = "/var/tmp/openvpn.status";
15my $JSON_OUTPUT_FILE = "/var/www/openvpn/status.json";
16
17my $srcip_table = {
18 'Destination 1' => [
19 '127.0.0.0/8',
20 ],
21 'Peer 2' => [
22 '8.8.0.0/16', '1.2.3.0/18',
23 ],
24};
25
26my %netblocks;
27
28sub read_netmask_table($)
29{
30 my ($t) = @_;
31
32 foreach my $k (keys %$t) {
33 my $table = {};
34 foreach my $net (@{$$t{$k}}) {
35 my $block = new Net::Netmask($net);
36 $block->storeNetblock($table);
37 }
38 $netblocks{$k} = $table;
39 }
40}
41
42sub classify_srcip($)
43{
44 my ($ip) = @_;
45 foreach my $k (%netblocks) {
46 my $block = findNetblock($ip, $netblocks{$k});
47 if ($block) {
48 return $k;
49 }
50 }
51 return undef;
52}
53
54# read the openvpn.status file and parse it, return hash reference to
55# its contents.
56sub get_openvpn_clients($)
57{
58 my ($fname) = @_;
59 my $state = 'init';
60 my $href;
61 my @clients;
62
63 $$href{version} = 1;
64
65 open(INFILE, "<", $fname);
66 while (my $line = <INFILE>) {
67 chomp($line);
68 if ($line =~ /^OpenVPN CLIENT LIST$/) {
69 $state = 'client_list';
70 } elsif ($line =~ /^ROUTING\ TABLE$/) {
71 $state = 'routing_table';
72 } else {
73 if ($state eq 'client_list') {
74 my %cl;
75 if ($line =~ /^Updated,(.*)/) {
76 $$href{updated} = $1;
77 } elsif ($line =~ /^(\S+),([0-9\.]+)\:(\d+),(\d+),(\d+),(.*)$/) {
78 $cl{name} = $1;
79 $cl{srcip} = $2;
80 $cl{operator} = classify_srcip($2);
81 $cl{srcport} = $3 + 0;
82 $cl{bytes_rx} = $4 + 0;
83 $cl{bytes_tx} = $5 + 0;
84 $cl{connected_since} = $6;
85 push(@clients, \%cl);
86 }
87 }
88 }
89 }
90 close(INFILE);
91
92 $$href{clients} = \@clients;
93
94 return $href;
95}
96
97# inotify handler to re-parse/convert openvpn.status on any change
98sub status_in_handler
99{
100 my $e = shift;
101
102 # read/parse openvpn.status
103 my $cl = get_openvpn_clients($e->fullname);
104
105 # write result to file
106 open(OUTFILE, ">", $JSON_OUTPUT_FILE);
107 print(OUTFILE to_json($cl, { pretty => 1 }));
108 close(OUTFILE);
109
110 # also print it to console for debugging
111 print(to_json($cl, { pretty => 1 }));
112}
113
114
115
116# main
117
118read_netmask_table($srcip_table);
119
120my $inotify = new Linux::Inotify2 or die("Can't create inotify object: $!");
121$inotify->watch($OPENVPN_STATE_FILE, IN_MODIFY, \&status_in_handler);
122
123# endless loop, wait for inotify enents
1241 while $inotify->poll;