#!/usr/bin/perl -w
# -*- perl -*-

=head1 NAME

pf_tables : Munin plugin to monitor pf tables.
Inout: bandwidth usage for table
Addresses: number of entries in table


=head1 APPLICABLE SYSTEMS

Should work on any BSD that has pf(4).

Examples:

=over

=item pf_tables_inout_tablename

=item pf_tables_addresses_authenticated

=item pf_tables_addresses_badboys


=head1 CONFIGURATION

[pf_tables_*]
user root

=head1 INTERPRETATION

The plugin simply runs the pfctl -sTables -vvv command and counts the number of
Addresses and InBytes/OutBytes in each table.

=head1 BUGS

Only tested extensively on FreeBSD.

=head1 MAGIC MARKERS

  #%# family=auto
  #%# capabilities=autoconf suggest

=head1 VERSION

  $Id$

=head1 AUTHOR

Copyright (C) 2015.

Original version by Luc Duchosal (at) arcantel (dot) ch.
Created by Luc Duchosal, 2015

=head1 LICENSE

BSD

=cut


use strict;
use Munin::Plugin;

$0 =~ /pf_tables_(addresses|inout)_(.+)$/;
my $name = $2;
my $operation = $1;

if ( defined($ARGV[0])) {
    if ($ARGV[0] eq 'autoconf') {
        print "yes\n";
        exit 0;
    }

    if ($ARGV[0] eq "config") {

        if (!defined($name)) {
            print "Unknown table\n";
            exit 0;
        }

        if (!defined($operation)) {
            print "Unknown operation\n";
            exit 0;
        }

        if ($operation =~ m/addresses/) {

            print "graph_title Connected users ($name)\n";
            print "graph_args --base 1000 -l 0\n";
            print "graph_vlabel Users\n";
            print "graph_scale no\n";
            print "graph_category network\n";
            print "graph_printf %3.0lf\n";

            print "users.label users\n";
            print "users.draw AREASTACK\n";
            print "users.colour 00C000\n";
            foreach my $field (qw(users)) {
                print_thresholds($field);
            }
        }

        if ($operation =~ m/inout/) {

            print "graph_title Network bandwidth ($name)\n";
            print "graph_args --base 1024 -l 0\n";
            print "graph_vlabel Bandwidth\n";
            print "graph_scale yes\n";
            print "graph_category network\n";
#            print "graph_printf %3.0lf\n";

            print "in.label in\n";
            print "in.type DERIVE\n";
            print "in.draw AREA\n";
            print "in.colour C00000\n";
            print "in.cdef in,8,*\n";
            print "in.min 0\n";
            print "in.graph no\n";
            print "out.label bps\n";
            print "out.type DERIVE\n";
            print "out.negative in\n";
            print "out.draw AREA\n";
            print "out.colour COLOUR18\n";
            print "out.cdef out,8,*\n";
            print "out.min 0\n";

            foreach my $field (qw(in out)) {
                print_thresholds($field);
            }

        }
        exit 0;
    }

    if ($ARGV[0] eq "suggest") {
        my %tables = &tables();
        foreach my $key (keys(%tables)) {
            print "addresses_$key\n";
            print "inout_$key\n";
        }
        exit 0;
    }

}

if (!defined($name)) {
    print "Usage: pf_tables_addresses_tablename or pf_tables_inout_tablename\n";
    exit 1;
}

my %tables = &tables();
if (!exists $tables{$name}) {
   print "Unknown table name $name\n";
   exit 2;
}

if ($operation =~ m/addresses/) {
    my $users = $tables{$name}->{"addresses"};
    print "users.value $users\n";
}

if ($operation =~ m/inout/) {
    my $in = $tables{$name}->{"inpassbytes"};
    my $out = $tables{$name}->{"outpassbytes"};
    print "in.value $in\n";
    print "out.value $out\n";
}


sub tables {

    # # pfctl -s Tables -vv
    # -pa-r-- auth
    #         Addresses:   0
    #         Cleared:     Fri Sep 18 17:34:42 2015
    #         References:  [ Anchors: 0                  Rules: 14                 ]
    #         Evaluations: [ NoMatch: 43624              Match: 788                ]
    #         In/Block:    [ Packets: 0                  Bytes: 0                  ]
    #         In/Pass:     [ Packets: 30908              Bytes: 2704516            ]
    #         In/XPass:    [ Packets: 124                Bytes: 7897               ]
    #         Out/Block:   [ Packets: 0                  Bytes: 0                  ]
    #         Out/Pass:    [ Packets: 30288              Bytes: 26313114           ]
    #         Out/XPass:   [ Packets: 89                 Bytes: 21166              ]

    my $output = `/sbin/pfctl -s Tables -vv 2> /dev/null`;
    my %tables;
    my $name;

    foreach (split(/\n/, $output)) {

       if (m|^[cpairhC\-]{7}\s+(\S+)$|) {
          $name = $1;
		  $name =~ s/\-/_/;
          $tables{$name}->{"name"} = $name;
          next;
       }

       if (m|Addresses:\s+([0-9]+)$|) {
          $tables{$name}->{"addresses"} = $1;
          next;
       }

       if (m|Cleared:\s+(.+)$|) {
          $tables{$name}->{"cleared"} = $1;
          next;
       }

       if (m|In/Block:\s+\[\s+Packets:\s+([0-9]+)\s+Bytes:\s+([0-9]+)\s+\]$|) {
          $tables{$name}->{"inblockpackets"} = $1;
          $tables{$name}->{"inblockbytes"} = $2;
          next;
       }

       if (m|In/Pass:\s+\[\s+Packets:\s+([0-9]+)\s+Bytes:\s+([0-9]+)\s+\]$|) {
          $tables{$name}->{"inpasspackets"} = $1;
          $tables{$name}->{"inpassbytes"} = $2;
          next;
       }

       if (m|In/XPass:\s+\[\s+Packets:\s+([0-9]+)\s+Bytes:\s+([0-9]+)\s+\]$|) {
          $tables{$name}->{"inxpasspackets"} = $1;
          $tables{$name}->{"inxpassbytes"} = $2;
          next;
       }

       if (m|Out/Block:\s+\[\s+Packets:\s+([0-9]+)\s+Bytes:\s+([0-9]+)\s+\]$|) {
          $tables{$name}->{"outblockpackets"} = $1;
          $tables{$name}->{"outblockbytes"} = $2;
          next;
       }

       if (m|Out/Pass:\s+\[\s+Packets:\s+([0-9]+)\s+Bytes:\s+([0-9]+)\s+\]$|) {
          $tables{$name}->{"outpasspackets"} = $1;
          $tables{$name}->{"outpassbytes"} = $2;
          next;
       }

       if (m|Out/XPass:\s+\[\s+Packets:\s+([0-9]+)\s+Bytes:\s+([0-9]+)\s+\]$|) {
          $tables{$name}->{"outxpasspackets"} = $1;
          $tables{$name}->{"outxpassbytes"} = $2;
          next;
       }

    }

    return %tables;

}

# vim:syntax=perl
