#!/bin/bash
#
# $Id: log_traffic,v 1.8.2.1 2002/01/03 03:15:37 pbaltz Exp $
# Copyright 2000-2002 Sun Microsystems, Inc.  All rights reserved. 
#  
# This script maintains and logs the activity on the ip accounting rules.
# It updates the accounting rules based on the currently running interfaces,
# and then captures the current use, logs that, zeros the counters.
#
# This updates the accounting rules and logs as often as it is run. 
# Therefore this frequency determines the amount of resolution you will
# get in the logs and how long it will take for an ip to get accounted for
# after it is added.

# Hardcode locale to be English, otherwise we may utilities may output Japanese strings that would mess up our parsing.
LC_ALL="en_US"

# don't run during build process
/bin/hostname |/bin/grep mfg.cobaltmicro.com &>/dev/null && exit 0 

STATUSFILE="/var/state/acct/last"         # last count on basic chains
LOGFILE="/var/log/ipacct"                 # log file we generate
TRAFFICFILE="/var/state/acct/traffic"     # current traffic we parse
FWCONFIGFILE="/etc/ipchains.conf"					# ipchains rules configuration
IPCHAINS="/sbin/ipchains"
IPTABLES="/usr/bin/iptables"
IFCONFIG="/sbin/ifconfig"


if [ -x $IPTABLES ]; then
	export TABLES=1
	FWCONFIGFILE="/etc/iptables.conf"	# much like the ipchains rules configuration
fi

# Update the accounting rules and the ipchains/tables config file
if [ $TABLES ]; then
	echo "# $FWCONFIGFILE
# This file is automatically generated by log_traffic.
# Any manual changes will be lost
$IPTABLES -N acctin > /dev/null 2>&1
$IPTABLES -N acctout > /dev/null 2>&1
$IPTABLES -F acctin
$IPTABLES -F acctout
$IPTABLES -I INPUT 1 -j acctin
$IPTABLES -I OUTPUT 1 -j acctout" > $FWCONFIGFILE

else
	echo "# $FWCONFIGFILE
# This file is automatically generated by log_traffic.
# Any manual changes will be lost
$IPCHAINS -N acctin
$IPCHAINS -N acctout
$IPCHAINS -F acctin
$IPCHAINS -F acctout
$IPCHAINS -I input 2 -j acctin
$IPCHAINS -I output 2 -j acctout" > $FWCONFIGFILE

fi

# Get a list of the ips that are currently being accounted for (LAST_IPS)
# and a list of the ips that should be monitored (UP_IPS).

if [ $TABLES ]; then
	LAST_IPS=`$IPTABLES -L acctin -vn | awk '/all.*[[:digit:]]/ { print $8 }'` 
else
	LAST_IPS=`$IPCHAINS -L acctin -vn | awk '/all.*[[:digit:]]/ { print $10 }'` 
fi

UP_IPS=`$IFCONFIG | grep "inet addr" | cut -f 2 -d":" | cut -f 1 -d" "`

# Add rules for ips that are in use but not accounted for,
# and add rules to config file for all IPs in use
for ip in $UP_IPS; do
	if [ $TABLES ]; then
		echo $IPTABLES -I acctin -d "$ip/32" >> $FWCONFIGFILE
		echo $IPTABLES -I acctout -s "$ip/32" >> $FWCONFIGFILE
	else
		echo $IPCHAINS -I acctout -s "$ip/32" >> $FWCONFIGFILE
	fi
	
	if [ -z "`echo $LAST_IPS | grep $ip`" ]; then
		if [ $TABLES ]; then
			$IPTABLES -I acctin -d $ip/32
			$IPTABLES -I acctout -s $ip/32
		else
			$IPCHAINS -I acctin -d $ip/32 
			$IPCHAINS -I acctout -s $ip/32 
		fi
	fi
done

# Delete rules for ips that are no longer in use
for ip in $LAST_IPS; do
	# FIXME: this test is not right.  Try grepping 1.2.3.4 from 1.2.3.40.  oops 
	if [ -z "`echo $UP_IPS | grep $ip`" ]; then
		if [ $TABLES ]; then
			$IPTABLES -D acctin -d $ip/32
			$IPTABLES -D acctout -s $ip/32
		else
			$IPCHAINS -D acctin -d $ip/32
			$IPCHAINS -D acctout -s $ip/32
		fi
	fi
done

# Now capture the lastest traffic and log it

# zero and record the usage in one step so we don't miss a packet
if [ $TABLES ]; then
	$IPTABLES -Z -L -xvn > $TRAFFICFILE
else
	$IPCHAINS -Z -L -xvn > $TRAFFICFILE
fi
DATE=`date +"[%d/%b/%Y:%H:%M:%S %z]"`

# you can't zero the default chains,
# so we maintain the previous values in a file so we can do diffs 
if [ ! -e $STATUSFILE ]; then
	touch $STATUSFILE
	INITIALIZE=1
fi

. $STATUSFILE
OLD_INPUT=$INPUT
OLD_FORWARD=$FORWARD
OLD_OUTPUT=$OUTPUT

###### iptables format
# Chain OUTPUT (policy ACCEPT 79 packets, 11646 bytes)
#     pkts      bytes target     prot opt in     out     source               destination         
#       79    11646 acctout    all  --  *      *       0.0.0.0/0            0.0.0.0/0          
#       79    11646 acctout    all  --  *      *       0.0.0.0/0            0.0.0.0/0          
#       79    11646 acctout    all  --  *      *       0.0.0.0/0            0.0.0.0/0          
# 
# Chain acctin (24 references)
#     pkts      bytes target     prot opt in     out     source               destination         
#        0        0            all  --  *      *       0.0.0.0/0            127.0.0.1          
#      459    24108            all  --  *      *       0.0.0.0/0            10.9.28.134        
# 
# Chain acctout (24 references)
#     pkts      bytes target     prot opt in     out     source               destination         
#        0        0            all  --  *      *       127.0.0.1            0.0.0.0/0          
#        0        0            all  --  *      *       127.0.0.1            0.0.0.0/0          
#      237    34938            all  --  *      *       10.9.28.134          0.0.0.0/0          
#      237    34938            all  --  *      *       10.9.28.134          0.0.0.0/0   
######
	
# parse the traffic file and update the log file
cat $STATUSFILE | awk '

	END {
		while (getline <trafficfile > 0) {
			line = $0
			if ( $line ~ /Chain.*\(/) {
				chain = $2
				bytes = $7

				if ( chain ~ /(input)|(output)|(forward)|(INPUT)|(OUTPUT)|(FORWARD)/ ) {
					traffic[chain] += bytes
        	fastforward = 1 # fastforward means skip to next chain
				} else {
					fastforward = 0 # otherwise look at the lines for this chain
				}
			}

			if ( (line ~ /all/) && ( fastforward == 0 ) ) {
				bytes = $2
				if ( chain ~ /acctin/ ) {
					if ( tables ) {
						ip = $8
					} else {
						ip = $10
					}
					traffic["acctin"] += bytes
				} else {
					traffic["acctout"] += bytes
					if ( tables ) {
						ip = $7
					} else {
						ip = $9
					}
				}
				printf("%s - - %s \"GET /%s HTTP/1.1\" 200 %s \"\" \"\"\n", ip, date, chain, bytes)
			}				

		}

		if ( ! initialize ) {
		#  * "other" traffic is traffic not destined for a specific ip	
		#  * we subtract the values from the last run for the default chains
		if ( ! tables) {
    			# ipchains wont let you zero these
			traffic["otherin"] = traffic["input"] - oldin - traffic["acctin"]
			traffic["otherout"] = traffic["output"] - oldout - traffic["acctout"]
			traffic["otherforward"] = traffic["forward"] - oldforward 
		} else {
			traffic["otherin"] = traffic["INPUT"] - traffic["acctin"]
			traffic["otherout"] = traffic["OUTPUT"] - traffic["acctout"]
			traffic["otherforward"] = traffic["FORWARD"]
		}

		printf("Other - - %s \"GET /input HTTP/1.1\" 200 %s \"\" \"\"\n", date, traffic["otherin"])
		printf("Other - - %s \"GET /output HTTP/1.1\" 200 %s \"\" \"\"\n", date, traffic["otherout"])
		printf("Other - - %s \"GET /forward HTTP/1.1\" 200 %s \"\" \"\"\n", date, traffic["otherforward"])
		}

		# update our records for the last value of the default chains
		printf("INPUT=%s\nFORWARD=%s\nOUTPUT=%s\n", traffic["input"], traffic["forward"], traffic["output"]) >statusfile

	}' trafficfile=$TRAFFICFILE logfile=$LOGFILE tables=$TABLES statusfile=$STATUSFILE oldin=$OLD_INPUT oldout=$OLD_OUTPUT oldforward=$OLD_FORWARD "date=$DATE" initialize=$INITIALIZE >> $LOGFILE

