From patchwork Sat Apr 6 04:29:37 2019 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Tim FitzGeorge X-Patchwork-Id: 2180 Return-Path: Received: from mail01.ipfire.org (mail01.i.ipfire.org [172.28.1.200]) (using TLSv1.2 with cipher ECDHE-ECDSA-AES256-GCM-SHA384 (256/256 bits)) (Client CN "mail01.ipfire.org", Issuer "Let's Encrypt Authority X3" (verified OK)) by web07.i.ipfire.org (Postfix) with ESMTPS id 48079861F37 for ; Fri, 5 Apr 2019 18:31:05 +0100 (BST) Received: from mail01.i.ipfire.org (localhost [IPv6:::1]) by mail01.ipfire.org (Postfix) with ESMTP id 44bRgD5Fd5z5Lcxs; Fri, 5 Apr 2019 18:31:04 +0100 (BST) Received: from smtp.hosts.co.uk (smtp.hosts.co.uk [85.233.160.19]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (Client did not present a certificate) by mail01.ipfire.org (Postfix) with ESMTPS id 44bRft12Ymz5LcyC for ; Fri, 5 Apr 2019 18:30:46 +0100 (BST) Received: from [31.127.205.161] (helo=aragorn.tfitzgeorge.me.uk) by smtp.hosts.co.uk with esmtpa (Exim) (envelope-from ) id 1hCSfq-00006W-6R; Fri, 05 Apr 2019 18:30:45 +0100 From: Tim FitzGeorge To: development@lists.ipfire.org Subject: [PATCH 09/12] statusmail: Other plugins Date: Fri, 5 Apr 2019 18:29:37 +0100 Message-Id: <20190405172940.13168-10-ipfr@tfitzgeorge.me.uk> X-Mailer: git-send-email 2.16.4 In-Reply-To: <20190405172940.13168-1-ipfr@tfitzgeorge.me.uk> References: <20190405172940.13168-1-ipfr@tfitzgeorge.me.uk> X-Spamd-Result: default: False [-13.17 / 11.00]; ARC_NA(0.00)[]; RCVD_VIA_SMTP_AUTH(0.00)[]; FROM_EQ_ENVFROM(0.00)[]; FROM_HAS_DN(0.00)[]; TO_DN_SOME(0.00)[]; R_SPF_ALLOW(-0.20)[+ip4:85.233.160.19]; MIME_GOOD(-0.10)[text/plain]; RCVD_TLS_LAST(0.00)[]; REPLY(-4.00)[]; DMARC_NA(0.00)[tfitzgeorge.me.uk]; TO_MATCH_ENVRCPT_SOME(0.00)[]; MX_GOOD(-0.01)[cached: mx1.ukservers.net]; RCPT_COUNT_TWO(0.00)[2]; MID_CONTAINS_FROM(1.00)[]; NEURAL_HAM(-2.99)[-0.997,0]; IP_SCORE(-3.77)[ip: (-9.91), ipnet: 85.233.160.0/19(-4.96), asn: 8622(-3.96), country: GB(-0.04)]; RCVD_IN_DNSWL_LOW(-0.10)[19.160.233.85.list.dnswl.org : 127.0.5.1]; R_DKIM_NA(0.00)[]; MIME_TRACE(0.00)[0:+]; ASN(0.00)[asn:8622, ipnet:85.233.160.0/19, country:GB]; RCVD_COUNT_TWO(0.00)[2]; BAYES_HAM(-3.00)[100.00%]; RECEIVED_SPAMHAUS_PBL(0.00)[161.205.127.31.zen.spamhaus.org : 127.0.0.11] Authentication-Results: mail01.ipfire.org; dkim=none; dmarc=none; spf=pass (mail01.ipfire.org: domain of ipfr@tfitzgeorge.me.uk designates 85.233.160.19 as permitted sender) smtp.mailfrom=ipfr@tfitzgeorge.me.uk X-BeenThere: development@lists.ipfire.org X-Mailman-Version: 2.1.15 Precedence: list List-Id: IPFire development talk List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: development-bounces@lists.ipfire.org Sender: "Development" Note that the graphs plugin requires a change to the existing graphs.pl to allow for arbitary time periods. Signed-off-by: Tim FitzGeorge --- src/statusmail/plugins/graphs.pm | 697 +++++++++++++++++++++++++ src/statusmail/plugins/hardware_media_space.pm | 154 ++++++ src/statusmail/plugins/network_firewall.pm | 357 +++++++++++++ 3 files changed, 1208 insertions(+) create mode 100644 src/statusmail/plugins/graphs.pm create mode 100644 src/statusmail/plugins/hardware_media_space.pm create mode 100644 src/statusmail/plugins/network_firewall.pm diff --git a/src/statusmail/plugins/graphs.pm b/src/statusmail/plugins/graphs.pm new file mode 100644 index 000000000..5b72c6e1a --- /dev/null +++ b/src/statusmail/plugins/graphs.pm @@ -0,0 +1,697 @@ +#!/usr/bin/perl + +############################################################################ +# # +# Send log and status emails for IPFire # +# # +# This is free software; you can redistribute it and/or modify # +# it under the terms of the GNU General Public License as published by # +# the Free Software Foundation; either version 3 of the License, or # +# (at your option) any later version. # +# # +# This 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. # +# # +# You should have received a copy of the GNU General Public License # +# along with IPFire; if not, write to the Free Software # +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA # +# # +# Copyright (C) 2018 - 2019 The IPFire Team # +# # +############################################################################ + +use strict; +use warnings; + +require "${General::swroot}/lang.pl"; +require "${General::swroot}/graphs.pl"; + +package Graphs; + +############################################################################ +# Function prototypes +############################################################################ + +sub add_graph( $$$$@ ); + +############################################################################ +# BEGIN Block +# +# Register the graphs available in this file. +# +# Note that some graphs are only available under certain circumstances, so +# it's necessary to check the circumstances apply. +############################################################################ + +sub BEGIN +{ + my %netsettings; + my %mainsettings; + + &General::readhash("${General::swroot}/ethernet/settings", \%netsettings); + &General::readhash("${General::swroot}/main/settings", \%mainsettings); + + my $config_type = $netsettings{'CONFIG_TYPE'}; + + my %common_options = ( 'section' => $Lang::tr{'graph'}, + 'format' => 'html' ); + + #---------------------------------------------------------------------------- + # Network + + if ($netsettings{'RED_TYPE'} ne 'PPPOE') + { + if ($netsettings{'RED_DEV'} ne $netsettings{'GREEN_DEV'}) + { + if ($netsettings{'RED_DEV'} eq 'red0') + { + main::add_mail_item( %common_options, + 'ident' => 'graph-network-red0', + 'subsection' => $Lang::tr{'interfaces'}, + 'item' => 'red0', + 'function' => \&red0 ); + } + else + { + main::add_mail_item( %common_options, + 'ident' => 'graph-network-ppp0', + 'subsection' => $Lang::tr{'interfaces'}, + 'item' => 'ppp0', + 'function' => \&ppp0 ); + } + } + } + else + { + main::add_mail_item( %common_options, + 'ident' => 'graph-network-ppp0', + 'subsection' => $Lang::tr{'interfaces'}, + 'item' => 'ppp0', + 'function' => \&ppp0 ); + } + + main::add_mail_item( %common_options, + 'ident' => 'graph-network-green0', + 'subsection' => $Lang::tr{'interfaces'}, + 'item' => 'green0', + 'function' => \&green0 ); + + if ($config_type == 3 or $config_type == 4) + { + # BLUE + main::add_mail_item( %common_options, + 'ident' => 'graph-network-blue0', + 'subsection' => $Lang::tr{'interfaces'}, + 'item' => 'blue0', + 'function' => \&blue0 ); + } + + if ($config_type == 2 or $config_type == 4) + { + # ORANGE + main::add_mail_item( %common_options, + 'ident' => 'graph-network-orange0', + 'subsection' => $Lang::tr{'interfaces'}, + 'item' => 'orange0', + 'function' => \&orange0 ); + } + + + if (-e "/var/log/rrd/collectd/localhost/interface/if_octets-ipsec0.rrd") + { + main::add_mail_item( %common_options, + 'ident' => 'graph-network-ipsec0', + 'subsection' => $Lang::tr{'network'}, + 'item' => 'ipsec0', + 'function' => \&ipsec0 ); + } + + if (-e "/var/log/rrd/collectd/localhost/interface/if_octets-tun0.rrd") + { + main::add_mail_item( %common_options, + 'ident' => 'graph-network-tun0', + 'subsection' => $Lang::tr{'network'}, + 'item' => 'tun0', + 'function' => \&tun0 ); + } + + main::add_mail_item( %common_options, + 'ident' => 'graph-network-fwhits', + 'subsection' => $Lang::tr{'network'}, + 'item' => $Lang::tr{'firewallhits'}, + 'function' => \&fw_hits ); + + #---------------------------------------------------------------------------- + # System + + main::add_mail_item( %common_options, + 'ident' => 'graph-system-cpu-usage', + 'subsection' => $Lang::tr{'system'}, + 'item' => "CPU $Lang::tr{'graph'}", + 'function' => \&cpu_usage ); + + main::add_mail_item( %common_options, + 'ident' => 'graph-system-cpu-load', + 'subsection' => $Lang::tr{'system'}, + 'item' => "Load $Lang::tr{'graph'}", + 'function' => \&cpu_load ); + + if ( -e "$mainsettings{'RRDLOG'}/collectd/localhost/cpufreq/cpufreq-0.rrd") + { + main::add_mail_item( %common_options, + 'ident' => 'graph-system-cpu-frequency', + 'subsection' => $Lang::tr{'system'}, + 'item' => "CPU $Lang::tr{'frequency'}", + 'function' => \&cpu_freq ); + } + + main::add_mail_item( %common_options, + 'ident' => 'graph-system-entropy', + 'subsection' => $Lang::tr{'system'}, + 'item' => $Lang::tr{'entropy'}, + 'function' => \&entropy ); + + #---------------------------------------------------------------------------- + # Hardware + + main::add_mail_item( %common_options, + 'ident' => 'graph-hardware-cpu-load', + 'subsection' => $Lang::tr{'hardware graphs'}, + 'item' => "Load $Lang::tr{'graph'}", + 'function' => \&cpu_load ); + + if ( `ls $mainsettings{'RRDLOG'}/collectd/localhost/thermal-thermal_zone* 2>/dev/null` ) + { + main::add_mail_item( %common_options, + 'ident' => 'graph-hardware-acpi-zone-temp', + 'subsection' => $Lang::tr{'hardware graphs'}, + 'item' => "ACPI Thermal-Zone Temp", + 'function' => \&therm ); + } + + if ( `ls $mainsettings{'RRDLOG'}/collectd/localhost/sensors-*/temperature-* 2>/dev/null` ) + { + main::add_mail_item( %common_options, + 'ident' => 'graph-hardware-temp', + 'subsection' => $Lang::tr{'hardware graphs'}, + 'item' => "hwtemp", + 'function' => \&hwtemp ); + } + + if ( `ls $mainsettings{'RRDLOG'}/collectd/localhost/sensors-*/fanspeed-* 2>/dev/null` ) + { + main::add_mail_item( %common_options, + 'ident' => 'graph-hardware-fan', + 'subsection' => $Lang::tr{'hardware graphs'}, + 'item' => "hwfan", + 'function' => \&hwfan ); + } + + if ( `ls $mainsettings{'RRDLOG'}/collectd/localhost/sensors-*/voltage-* 2>/dev/null` ) + { + main::add_mail_item( %common_options, + 'ident' => 'graph-hardware-volt', + 'subsection' => $Lang::tr{'hardware graphs'}, + 'item' => "hwvolt", + 'function' => \&hwvolt ); + } + + #---------------------------------------------------------------------------- + # Memory + + main::add_mail_item( %common_options, + 'ident' => 'graph-memory-memory', + 'subsection' => $Lang::tr{'memory'}, + 'item' => $Lang::tr{'memory'}, + 'function' => \&memory ); + + main::add_mail_item( %common_options, + 'ident' => 'graph-memory-swap', + 'subsection' => $Lang::tr{'memory'}, + 'item' => $Lang::tr{'swap'}, + 'function' => \&swap ); + + #---------------------------------------------------------------------------- + # Disks + + foreach my $path (glob '/var/log/rrd/collectd/localhost/disk*') + { + my ($name) = $path =~ m/disk\-(\w+)/; + + main::add_mail_item( %common_options, + 'ident' => "graph-disk-access-$name", + 'subsection' => $Lang::tr{'statusmail disk access'}, + 'item' => $name, + 'function' => sub { my ($this) = @_; diskaccess( $this, $name ); } ); + + main::add_mail_item( %common_options, + 'ident' => "graph-disk-temp-$name", + 'subsection' => $Lang::tr{'statusmail disk temperature'}, + 'item' => $name, + 'function' => sub { my ($this) = @_; disktemp( $this, $name ); } ); + } + +# Other graphs that aren't available. +# updatepinggraph( host, period ) : netother.cgi +# updateprocessescpugraph( period ) +# updateprocessesmemorygraph( period ) +# updateqosgraph( device, period ) red0 | ppp0 | imq0 : qos.cgi +# updatevpngraph( interface, period ) : netovpnrw.cgi +# updatevpnn2ngraph( interface, period ) : netovpnsrv.cgi +# updatewirelessgraph( interface, period ) +} + +############################################################################ +# Code +############################################################################ + +#------------------------------------------------------------------------------ +# sub add_graph( object, function, name, alternate[, params...] ) +# +# Adds a graph to the mail message. This runs a sub-process to capture the +# output from running the standard WUI's graphing function, which is sent to +# stdout. +# +# Parameters: +# this message object +# function function producing graph +# name name of graph file +# alternate alternate text for image +# params parameters to be passed to graph function +#------------------------------------------------------------------------------ + +sub add_graph( $$$$@ ) +{ + my ($this, $function, $name, $alternate, @params) = @_; + + my $from_child; + + my $pid = open( $from_child, "-|" ); + + if ($pid) + { # parent + binmode $from_child; + + $this->add_image( fh => $from_child, + alt => $alternate, + type => 'image/png', + name => $name ); + + waitpid( $pid, 0 ); + close $from_child; + } + else + { # child + binmode( STDOUT ); + + my $period = $this->get_period(); + + &$function( @params, $period ); + + exit; + } +} + + +#------------------------------------------------------------------------------ +# sub ppp0( this ) +# +# Adds a graph of the ppp0 interface throughput +# +# Parameters: +# this message object +#------------------------------------------------------------------------------ + +sub ppp0( $ ) +{ + my ($this) = @_; + + add_graph( $this, \&Graphs::updateifgraph, 'ppp0_if.png', 'ppp0 interface throughput', 'ppp0' ); + + return 1; +} + + +#------------------------------------------------------------------------------ +# sub red0( this ) +# +# Adds a graph of the red0 interface throughput +# +# Parameters: +# this message object +#------------------------------------------------------------------------------ + +sub red0( $ ) +{ + my ($this) = @_; + + add_graph( $this, \&Graphs::updateifgraph, 'red0_if.png', 'red0 interface throughput', 'red0' ); + + return 1; +} + + +#------------------------------------------------------------------------------ +# sub green0( this ) +# +# Adds a graph of the green0 interface throughput +# +# Parameters: +# this message object +#------------------------------------------------------------------------------ + +sub green0( $ ) +{ + my ($this) = @_; + + add_graph( $this, \&Graphs::updateifgraph, 'green0_if.png', 'green0 interface throughput', 'green0' ); + + return 1; +} + + +#------------------------------------------------------------------------------ +# sub blue0( this ) +# +# Adds a graph of the blue0 interface throughput +# +# Parameters: +# this message object +#------------------------------------------------------------------------------ + +sub blue0( $ ) +{ + my ($this) = @_; + + add_graph( $this, \&Graphs::updateifgraph, 'blue0_if.png', 'blue0 interface throughput', 'blue0' ); + + return 1; +} + + +#------------------------------------------------------------------------------ +# sub orange0( this ) +# +# Adds a graph of the orange0 interface throughput +# +# Parameters: +# this message object +#------------------------------------------------------------------------------ + +sub orange0( $ ) +{ + my ($this) = @_; + + add_graph( $this, \&Graphs::updateifgraph, 'orange0_if.png', 'orange0 interface throughput', 'orange0' ); + + return 1; +} + + +#------------------------------------------------------------------------------ +# sub ipsec0( this ) +# +# Adds a graph of the ipsec0 interface +# +# Parameters: +# this message object +#------------------------------------------------------------------------------ + +sub ipsec0( $ ) +{ + my ($this) = @_; + + add_graph( $this, \&Graphs::updateifgraph, 'ipsec0_if.png', 'ipsec0 interface throughput', 'ipsec0' ); + + return 1; +} + + +#------------------------------------------------------------------------------ +# sub tun0( this ) +# +# Adds a graph of the tun0 interface +# +# Parameters: +# this message object +#------------------------------------------------------------------------------ + +sub tun0( $ ) +{ + my ($this) = @_; + + add_graph( $this, \&Graphs::updateifgraph, 'tun0_if.png', 'tun0 interface throughput', 'tun0' ); + + return 1; +} + + +#------------------------------------------------------------------------------ +# sub cpu_usage( this ) +# +# Adds a graph of the CPU usage +# +# Parameters: +# this message object +#------------------------------------------------------------------------------ + +sub cpu_usage( $ ) +{ + my ($this) = @_; + + add_graph( $this, \&Graphs::updatecpugraph, 'cpu_usage.png', "CPU $Lang::tr{'graph'}" ); + + return 1; +} + + +#------------------------------------------------------------------------------ +# sub cpu_freq( this ) +# +# Adds a graph of the CPU frequency +# +# Parameters: +# this message object +#------------------------------------------------------------------------------ + +sub cpu_freq( $ ) +{ + my ($this) = @_; + + add_graph( $this, \&Graphs::updatecpufreqgraph, 'cpu_freq.png', "CPU $Lang::tr{'frequency'}" ); + + return 1; +} + + +#------------------------------------------------------------------------------ +# sub cpu_load( this ) +# +# Adds a graph of the CPU load +# +# Parameters: +# this message object +#------------------------------------------------------------------------------ + +sub cpu_load( $$ ) +{ + my ($this) = @_; + + add_graph( $this, \&Graphs::updateloadgraph,, 'cpu_load.png', "Load $Lang::tr{'graph'}" ); + + return 1; +} + + +#------------------------------------------------------------------------------ +# sub fw_hits( this ) +# +# Adds a graph of the Firewall hits +# +# Parameters: +# this message object +#------------------------------------------------------------------------------ + +sub fw_hits( $ ) +{ + my ($this) = @_; + + add_graph( $this, \&Graphs::updatefwhitsgraph, 'fw_hits.png', $Lang::tr{'firewallhits'} ); + + return 1; +} + + +#------------------------------------------------------------------------------ +# sub therm( this ) +# +# Adds a graph of the ACPI Thermal zone temperatures +# +# Parameters: +# this message object +#------------------------------------------------------------------------------ + +sub therm( $ ) +{ + my ($this) = @_; + + add_graph( $this, \&Graphs::updatethermaltempgraph, 'therm.png', "ACPI Thermal-Zone Temp" ); + + return 1; +} + + +#------------------------------------------------------------------------------ +# sub hwtemp( this ) +# +# Adds a graph of the Hardware Temperatures +# +# Parameters: +# this message object +#------------------------------------------------------------------------------ + +sub hwtemp( $ ) +{ + my ($this) = @_; + + add_graph( $this, \&Graphs::updatehwtempgraph, 'hw_temp.png', 'hwtemp' ); + + return 1; +} + + +#------------------------------------------------------------------------------ +# sub hwfan( this ) +# +# Adds a graph of the Fan Speeds +# +# Parameters: +# this message object +#------------------------------------------------------------------------------ + +sub hwfan( $ ) +{ + my ($this) = @_; + + add_graph( $this, \&Graphs::updatehwfangraph, 'hw_fan.png', 'hwfan' ); + + return 1; +} + + +#------------------------------------------------------------------------------ +# sub hwvolt( this ) +# +# Adds a graph of the Hardware voltages +# +# Parameters: +# this message object +#------------------------------------------------------------------------------ + +sub hwvolt( $ ) +{ + my ($this) = @_; + + add_graph( $this, \&Graphs::updatehwvoltgraph, 'hw_volt.png', 'hw volt' ); + + return 1; +} + + +#------------------------------------------------------------------------------ +# sub entropy( this ) +# +# Adds a graph of the Entropy +# +# Parameters: +# this message object +#------------------------------------------------------------------------------ + +sub entropy( $ ) +{ + my ($this) = @_; + + add_graph( $this, \&Graphs::updateentropygraph, 'entropy.png', $Lang::tr{'entropy'} ); + + return 1; +} + + +#------------------------------------------------------------------------------ +# sub memory( this ) +# +# Adds a graph of the memory usage +# +# Parameters: +# this message object +#------------------------------------------------------------------------------ + +sub memory( $ ) +{ + my ($this) = @_; + + add_graph( $this, \&Graphs::updatememorygraph, 'memory.png', $Lang::tr{'memory'} ); + + return 1; +} + + +#------------------------------------------------------------------------------ +# sub swap( this ) +# +# Adds a graph of the swapfile usage +# +# Parameters: +# this message object +#------------------------------------------------------------------------------ + +sub swap( $ ) +{ + my ($this) = @_; + + add_graph( $this, \&Graphs::updateswapgraph, 'swap.png', $Lang::tr{'swap'} ); + + return 1; +} + + +#------------------------------------------------------------------------------ +# sub diskaccess( this, name ) +# +# Adds a graph of the disk access rate +# +# Parameters: +# this message object +# name disk name +#------------------------------------------------------------------------------ + +sub diskaccess( $$ ) +{ + my ($this, $name) = @_; + + add_graph( $this, \&Graphs::updatediskgraph, "disk_access_$name.png", $name, $name ); + + return 1; +} + + +#------------------------------------------------------------------------------ +# sub updatehddgraph( this, name ) +# +# Adds a graph of the disk temperature +# +# Parameters: +# this message object +# name disk name +#------------------------------------------------------------------------------ + +sub disktemp( $$ ) +{ + my ($this, $name) = @_; + + add_graph( $this, \&Graphs::updatehddgraph, "disk_temp_$name.png", $name, $name ); + + return 1; +} diff --git a/src/statusmail/plugins/hardware_media_space.pm b/src/statusmail/plugins/hardware_media_space.pm new file mode 100644 index 000000000..ce3db2def --- /dev/null +++ b/src/statusmail/plugins/hardware_media_space.pm @@ -0,0 +1,154 @@ +#!/usr/bin/perl + +############################################################################ +# # +# Send log and status emails for IPFire # +# # +# This is free software; you can redistribute it and/or modify # +# it under the terms of the GNU General Public License as published by # +# the Free Software Foundation; either version 3 of the License, or # +# (at your option) any later version. # +# # +# This 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. # +# # +# You should have received a copy of the GNU General Public License # +# along with IPFire; if not, write to the Free Software # +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA # +# # +# Copyright (C) 2018 - 2019 The IPFire Team # +# # +############################################################################ + +use strict; +use warnings; + +require "${General::swroot}/lang.pl"; + +package Hardware_Media_Space; + +############################################################################ +# Function prototypes +############################################################################ + +sub space( $$ ); +sub inodes( $$ ); + +############################################################################ +# BEGIN Block +# +# Register the log items available in this file +############################################################################ + +sub BEGIN +{ + main::add_mail_item( 'ident' => 'hardware-media-space', + 'section' => $Lang::tr{'statusmail hardware'}, + 'subsection' => $Lang::tr{'media'}, + 'item' => $Lang::tr{'disk usage'}, + 'function' => \&space, + 'option' => { 'type' => 'integer', + 'name' => $Lang::tr{'statusmail max free percent'}, + 'min' => 0, + 'max' => 100 } ); + + main::add_mail_item( 'ident' => 'hardware-media-inodes', + 'section' => $Lang::tr{'statusmail hardware'}, + 'subsection' => $Lang::tr{'media'}, + 'item' => 'inodes', + 'function' => \&inodes, + 'option' => { 'type' => 'integer', + 'name' => $Lang::tr{'statusmail max free percent'}, + 'min' => 0, + 'max' => 100 } ); +} + +############################################################################ +# Code +############################################################################ + +#------------------------------------------------------------------------------ +# sub space( this, min_percent ) +# +# Adds the disk usage in terms of space used. +# +# Parameters: +# this message object +# min_percent Only display information if this amount of space or less is +# free +#------------------------------------------------------------------------------ + +sub space( $$ ) +{ + my $message = shift; + my $min_percent = 100 - shift; + my @lines; + + # Get the process information + + foreach my $line (`df -BM`) + { + my @fields = split /\s+/, $line, 6; + if ($fields[4] =~ m/\d+\%/) + { + my ($percent) = $fields[4] =~ m/(\d+)\%/; + next if ($percent <= $min_percent); + } + push @lines, [ @fields ]; + } + + if (@lines > 1) + { + $message->add_table( @lines ); + + return 1; + } + + return 0; +} + + +#------------------------------------------------------------------------------ +# sub inodes( this, min_percent ) +# +# Adds the disk usage in terms of inodes used. +# +# Parameters: +# this message object +# min_percent Only display information if this number of inodes or less is +# free +#------------------------------------------------------------------------------ + +sub inodes( $$ ) +{ + my $message = shift; + my $min_percent = 100 - shift; + my @lines; + + # Get the process information + + foreach my $line (`df -i`) + { + my @fields = split /\s+/, $line, 6; + next if ($fields[1] == 0); + if ($fields[4] =~ m/\d+\%/) + { + my ($percent) = $fields[4] =~ m/(\d+)\%/; + next if ($percent <= $min_percent); + } + push @lines, [ @fields ]; + } + + if (@lines > 1) + { + $message->add_table( @lines ); + + return 1; + } + + return 0; +} + +1; diff --git a/src/statusmail/plugins/network_firewall.pm b/src/statusmail/plugins/network_firewall.pm new file mode 100644 index 000000000..1abe4e482 --- /dev/null +++ b/src/statusmail/plugins/network_firewall.pm @@ -0,0 +1,357 @@ +#!/usr/bin/perl + +############################################################################ +# # +# Send log and status emails for IPFire # +# # +# This is free software; you can redistribute it and/or modify # +# it under the terms of the GNU General Public License as published by # +# the Free Software Foundation; either version 3 of the License, or # +# (at your option) any later version. # +# # +# This 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. # +# # +# You should have received a copy of the GNU General Public License # +# along with IPFire; if not, write to the Free Software # +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA # +# # +# Copyright (C) 2018 - 2019 The IPFire Team # +# # +############################################################################ + +require "${General::swroot}/lang.pl"; + +use strict; +use warnings; + +package Network_Firewall; + +use Time::Local; + +require "${General::swroot}/geoip-functions.pl"; + +############################################################################ +# BEGIN Block +# +# Register the log items available in this file +############################################################################ + +sub BEGIN +{ + main::add_mail_item( 'ident' => 'network-firewall-ipaddresses', + 'section' => $Lang::tr{'network'}, + 'subsection' => $Lang::tr{'firewall'}, + 'item' => $Lang::tr{'ip address'}, + 'function' => \&addresses, + 'option' => { 'type' => 'integer', + 'name' => $Lang::tr{'statusmail firewall min count'}, + 'min' => 1, + 'max' => 1000 } ); + + main::add_mail_item( 'ident' => 'network-firewall-ports', + 'section' => $Lang::tr{'network'}, + 'subsection' => $Lang::tr{'firewall'}, + 'item' => $Lang::tr{port}, + 'function' => \&ports, + 'option' => { 'type' => 'integer', + 'name' => $Lang::tr{'statusmail firewall min count'}, + 'min' => 1, + 'max' => 1000 } ); + + main::add_mail_item( 'ident' => 'network-firewall-countries', + 'section' => $Lang::tr{'network'}, + 'subsection' => $Lang::tr{'firewall'}, + 'item' => $Lang::tr{country}, + 'function' => \&countries, + 'option' => { 'type' => 'integer', + 'name' => $Lang::tr{'statusmail firewall min count'}, + 'min' => 1, + 'max' => 1000 } ); + + main::add_mail_item( 'ident' => 'network-firewall-reason', + 'section' => $Lang::tr{'network'}, + 'subsection' => $Lang::tr{'firewall'}, + 'item' => $Lang::tr{'statusmail firewall reason'}, + 'function' => \&reasons, + 'option' => { 'type' => 'integer', + 'name' => $Lang::tr{'statusmail firewall min count'}, + 'min' => 1, + 'max' => 1000 } ); +} + + +############################################################################ +# Functions +############################################################################ + +sub get_log( $ ); +sub addresses( $$ ); + +#------------------------------------------------------------------------------ +# sub get_log( this ) +# +# Gets information on blocked packets from the system log and caches it. +# +# Parameters: +# this message object +# +# Returns: +# reference to hash of information +#------------------------------------------------------------------------------ + +sub get_log( $ ) +{ + my ($this, $name) = @_; + + my $data = $this->cache( 'network-firewall' ); + + return $data if (defined $data); + + my %info; + my $line; + + while ($line = $this->get_message_log_line) + { + next unless ($line); + next unless ($line =~ m/kernel: DROP/); + + my ($time, $rule, $interface, $src_addrs, $dst_port) = + $line =~ m/(\w+\s+\d+\s+\d+:\d+:\d+).*DROP_(\w+?)\s*IN=(\w+).*SRC=(\d+\.\d+\.\d+\.\d+).*(?:DPT=(\d*))/; +# mmm dd hh:mm:dd ipfire kernel: DROP_SPAMHAUS_EDROPIN=ppp0 OUT= MAC= SRC=999.999.999.999 DST=888.888.888.888 LEN=40 TOS=0x00 PREC=0x00 TTL=248 ID=35549 PROTO=TCP SPT=47851 DPT=28672 WINDOW=1024 RES=0x00 SYN URGP=0 MARK=0xd2 + + next unless ($src_addrs); + + my $country = GeoIP::lookup( $src_addrs ) || $src_addrs; + + $info{'by_address'}{$src_addrs}{'count'}++; + $info{'by_address'}{$src_addrs}{'first'} = $time unless ($info{'by_address'}{$src_addrs}{'first'}); + $info{'by_address'}{$src_addrs}{'last'} = $time; + + if ($dst_port) + { + $info{'by_port'}{$dst_port}{'count'}++ ; + $info{'by_port'}{$dst_port}{'first'} = $time unless ($info{'by_port'}{$dst_port}{'first'}); + $info{'by_port'}{$dst_port}{'last'} = $time; + } + + if ($country) + { + $info{'by_country'}{$country}{'count'}++; + $info{'by_country'}{$country}{'first'} = $time unless ($info{'by_country'}{$country}{'first'}); + $info{'by_country'}{$country}{'last'} = $time; + } + + $info{'by_rule'}{$rule}{'count'}++; + $info{'by_rule'}{$rule}{'first'} = $time unless ($info{'by_rule'}{$rule}{'first'}); + $info{'by_rule'}{$rule}{'last'} = $time; + + $info{'total'}++; + }; + + $this->cache( 'network-firewall', \%info ); + + return \%info; +} + + +#------------------------------------------------------------------------------ +# sub addresses( this, min_count ) +# +# Output information on blocked addresses. +# +# Parameters: +# this message object +# min_count only output blocked addresses occurring at least this number of +# times +#------------------------------------------------------------------------------ + +sub addresses( $$ ) +{ + my ($self, $min_count) = @_; + my @table; + + use Sort::Naturally; + + push @table, ['|', '|', '|', '|', '|', '|']; + push @table, [ $Lang::tr{'ip address'}, $Lang::tr{'country'}, $Lang::tr{'count'}, $Lang::tr{'percentage'}, $Lang::tr{'first'}, $Lang::tr{'last'} ]; + + my $stats = get_log( $self ); + + foreach my $address (sort { $$stats{'by_address'}{$b}{'count'} <=> $$stats{'by_address'}{$a}{'count'} || + ncmp( $b, $a ) } keys %{ $$stats{'by_address'} } ) + { + my $count = $$stats{'by_address'}{$address}{'count'}; + my $country = GeoIP::lookup( $address ); + my $first = $$stats{'by_address'}{$address}{'first'}; + my $last = $$stats{'by_address'}{$address}{'last'}; + my $percent = int( 100 * $count / $$stats{'total'} + 0.5); + + last if ($count < $min_count); + + my $name = $self->lookup_ip_address( $address ); + + $address = "$address\n$name" if ($name); + + if ($country) + { + $country = GeoIP::get_full_country_name( $country) || $address; + } + else + { + $country = $Lang::tr{'unknown'}; + } + + push @table, [ $address, $country, $count, $percent, $first, $last ]; + + last if (@table > $self->get_max_lines_per_item + 2) + } + + if (@table > 2) + { + $self->add_table( @table ); + + return 1; + } + + return 0; +} + + +#------------------------------------------------------------------------------ +# sub ports( this, min_count ) +# +# Output information on blocked ports. +# +# Parameters: +# this message object +# min_count only output blocked ports occurring at least this number of +# times +#------------------------------------------------------------------------------ + +sub ports( $$ ) +{ + my ($self, $min_count) = @_; + my @table; + + push @table, ['|', '|', '|', '|', '|']; + push @table, [ $Lang::tr{'port'}, $Lang::tr{'count'}, $Lang::tr{'percentage'}, $Lang::tr{'first'}, $Lang::tr{'last'} ]; + + my $stats = get_log( $self ); + + foreach my $port (sort { $$stats{'by_port'}{$b}{'count'} <=> $$stats{'by_port'}{$a}{'count'} || + ncmp( $b, $a ) } keys %{ $$stats{'by_port'} } ) + { + my $count = $$stats{'by_port'}{$port}{'count'}; + my $first = $$stats{'by_port'}{$port}{'first'}; + my $last = $$stats{'by_port'}{$port}{'last'}; + my $percent = int( 100 * $count / $$stats{'total'} + 0.5); + + last if ($count < $min_count); + + push @table, [ $port, $count, $percent, $first, $last ]; + } + + if (@table > 2) + { + $self->add_table( @table ); + + return 1; + } + + return 0; +} + + +#------------------------------------------------------------------------------ +# sub countries( this, min_count ) +# +# Output information on blocked countries. +# +# Parameters: +# this message object +# min_count only output blocked countries occurring at least this number of +# times +#------------------------------------------------------------------------------ + +sub countries( $$ ) +{ + my ($self, $min_count) = @_; + my @table; + + push @table, ['<', '|', '|', '|', '|']; + push @table, [ $Lang::tr{'country'}, $Lang::tr{'count'}, $Lang::tr{'percentage'}, $Lang::tr{'first'}, $Lang::tr{'last'} ]; + + my $stats = get_log( $self ); + + foreach my $country (sort { $$stats{'by_country'}{$b}{'count'} <=> $$stats{'by_country'}{$a}{'count'} } keys %{ $$stats{'by_country'} } ) + { + my $count = $$stats{'by_country'}{$country}{'count'}; + my $first = $$stats{'by_country'}{$country}{'first'}; + my $last = $$stats{'by_country'}{$country}{'last'}; + my $percent = int( 100 * $count / $$stats{'total'} + 0.5); + + last if ($count < $min_count); + + my $full_country = GeoIP::get_full_country_name( $country) || $country; + + push @table, [ $full_country, $count, $percent, $first, $last ]; + } + + if (@table > 2) + { + $self->add_table( @table ); + + return 1; + } + + return 0; +} + + +#------------------------------------------------------------------------------ +# sub reasons( this, min_count ) +# +# Output information on blocked reasons (the IPtable blocking the packet). +# +# Parameters: +# this message object +# min_count only output blocked reasons occurring at least this number of +# times +#------------------------------------------------------------------------------ + +sub reasons( $$ ) +{ + my ($self, $min_count) = @_; + my @table; + + push @table, ['<', '|', '|', '|', '|']; + push @table, [ $Lang::tr{'statusmail firewall reason'}, $Lang::tr{'count'}, $Lang::tr{'percentage'}, $Lang::tr{'first'}, $Lang::tr{'last'} ]; + + my $stats = get_log( $self ); + + foreach my $reason (sort { $$stats{'by_rule'}{$b}{'count'} <=> $$stats{'by_rule'}{$a}{'count'} } keys %{ $$stats{'by_rule'} } ) + { + my $count = $$stats{'by_rule'}{$reason}{'count'}; + my $first = $$stats{'by_rule'}{$reason}{'first'}; + my $last = $$stats{'by_rule'}{$reason}{'last'}; + my $percent = int( 100 * $count / $$stats{'total'} + 0.5); + + last if ($count < $min_count); + + push @table, [ $reason, $count, $percent, $first, $last ]; + } + + if (@table > 2) + { + $self->add_table( @table ); + + return 1; + } + + return 0; +} + +1;