#!/usr/bin/perl
use warnings;
use strict;
$|=1;

my $opt_f = "/proc/net/tcp,/proc/net/tcp6"; # files to read from

my $opt_w = 500;
my $opt_c = 1000;

use Getopt::Long qw(:config no_ignore_case bundling);
GetOptions(
        'f=s' => \$opt_f,
        'w=s' => \$opt_w,
        'c=s' => \$opt_c,
        ) or die "funnyargs";

my ($msg, $perf, $scnt,);
my %STATS = ();
my $ec = 3;

#OK - TCP netstat (ESTABLISHED 10 / TIME_WAIT 11) | CW=0 CLO=0 EST=10 FW1=0 FW2=0 LA=0 SR=0 SS=0 TW=11;
my %SN = (
	 1 => "EST", # ESTABLISHED
	 2 => "SS", # SYN_SENT
	 3 => "SR", # SYN_RECV
	 4 => "FW1", # FIN_WAIT1
	 5 => "FW2", # FIN_WAIT2
	 6 => "TW", # TIME_WAIT
	 7 => "CLO", # CLOSE
	 8 => "CW", # CLOSE_WAIT
	 9 => "LA", # LAST_ACK
	10 => "LIS", # LISTEN
	11 => "CLI", # CLOSING

	"txq" => "TXQ",
	"rxq" => "RXQ",
	);

my @files = split ",", $opt_f;
my $rc = eval { 
	for my $f (@files) {
		next unless -e $f;
		&do_file($f); 
	}
	my %o = ();
	for (keys %SN) {
		$o{$SN{$_}} += (delete($STATS{$_})||0);
	}
	die "leftover data: ".join(keys %STATS) if keys %STATS;
	$perf = join " ", map({ sprintf "%s=%s;", $_, $o{$_}} sort keys %o);
	$msg = sprintf "%i sockets, %i files", $scnt, scalar @files;
	return "ok";
};

if (defined $rc && $rc eq 'ok') {
	$msg ||= sprintf "%i perfbytes", length $perf;
	$ec = 0;
	if ($opt_c && $scnt > $opt_c) {
		$msg = sprintf "CRIT( count %i > %i ) %s", $scnt, $opt_c, $msg;
		$ec = 2;
	} elsif ($opt_w && $scnt > $opt_w) {
		$msg = sprintf "WARN( count %i > %i ) %s", $scnt, $opt_w, $msg;
		$ec = 1;
	}
	
} else {
	$ec = 3;
	if ($@) {
		chomp $@;
		$msg = "EXCEPTION: '$@'";
	} else {
		$msg = "UNKNOWNRC: '$rc'";
	}
}
unless (defined $msg && length $msg) {
	$msg = "NOMESSAGE";
	$ec = 3;
}

#TODO
$perf ||= "";

my $st = {
		0 => 'OK',
		1 => 'WARNING',
		2 => 'CRITICAL',
		3 => 'UNKNOWN',
	}->{$ec}||"EC$ec";

printf "%s - %s|%s\n", $st, $msg, $perf;

exit $ec;


sub do_file($) {
	my ($fn,) = @_;

	die "'$fn' does not exist" unless -e $fn;
	die "'$fn' not a file" unless -f $fn;
	open IF, "<", $fn or die "open($fn): $!";

	my $d;
	while (<IF>) {
		s/[\s\r\n]+/ /g;
		s/(^ | $)//g;

# sl  local_address rem_address   st tx_queue rx_queue tr tm->when retrnsmt   uid  timeout inode                                                     
		if (/^sl \S+ \S+ st tx\S+ rx\S+ /) {
			# skip valid header
			next;
#  0: 00000000:EA86 00000000:0000 0A 00000000:00000000 00:00000000 00000000     0        0 13819 1 e189e700 299 0 0 2 -1                             
		} elsif (/^\S+ \S+ \S+ (\S\S) ([^:]+):([^:]+) /) {
			$STATS{ord(pack "H*", $1)}++;
			$STATS{txq} += unpack "N", pack "H*", $2;
			$STATS{rxq} += unpack "N", pack "H*", $3;
			$scnt++;
		} else {
			die "bad line: '$_'";
		}
	}
	close IF;

	return "ok";
}

