#!/bin/bash
#
#!F:reject-masq
#
#!P:/usr/local/sbin
#
#!D:Reject old connections after shutting down ISDN link
#!D: for kernel 2.1.102+ (usage of ipchains)
#
#!C:Copyright 1999-2001 by Peter Bieringer <pb@bieringer.de>
#!C: Idea taken from reject-masq.c (usage of ipfwadm)
#!C:  with Copyright (C) Jochen Roedenbeck 1998

# Changes to
#  1.01: first published release
#  1.02: also locally outgoing connections
#  1.03: do not block "localhost" connections, add log file output
# 20000129: add dial device
# 20000624: Adapt localnet
# 20010124 [feature]: get local network from local interfaces instead from a define
# 20010124 [bugfix]: also recognize outgoing UDP connections, fix lockfile output

#### Defines (perhaps to change)

# Dialout device
DIALOUTDEVICE="ippp+"

# default timeout in seconds
TIMEOUT=3600

#### Defines (normally not for changing)

# A debug value can be set here
DEBUG=$[ 65535 - 256 -128 -1 -512 -2 -4 -64]
#DEBUG=0
	# &    1: show given options
	# &    2: print option runtime echos
	# &    4: show connections to test
	# &   64: do not execute $BIN_IPCHAINS
	# &  128: use file instead of real connections
	# &  256: Exctraction of local network addresses
	# &  512: Show extracted  network addresses


# Prefix of names of local interfaces
NAMEPREFIXIFACE="^eth|^tr|^lo" 

# Prefix of the lock file
IPMASQLOCKPREFIX="MASQ"

## Define used binaries
BIN_IFCONFIG="/sbin/ifconfig"
BIN_IPCHAINS="/sbin/ipchains"
BIN_NETSTAT="/bin/netstat"

## Define of used tmpfiles
FILE_TMP="/tmp/reject-masq-ng.tmp"

# Log file name
FILELOG=/var/log/reject-masq

# Log active?
DOLOG=1
#DOLOG=0


#### Known bugs
# Works only with subnet masks of 8, 16 or 24 bit

#### ToDo
# Abstract rules to use also iptables (netfilter)

#### Description is taken from reject-masq.c
#
# If dynamic IP address allocation is used to connect the Internet
# all connections become invalid when the ISDN link is shut down.
# Nevertheless data for these old connections can be sent. This program
# installs firewall rules to reject these data and to prevent the ISDN
# system from calling out for a dead connection.
#
# usage:
# a) reject-masq --insert
#
#    /proc/net/ip_masquerade is read. For each line in this file a
#    forwarding firewall rule is installed to reject all packets
#    for the connection. A file /var/run/MASQ.* is written containing
#    the options for deleting the installed firewall rules.
#
#    This command should be used in /etc/ppp/ip-down.
#
# b) reject-masq --delete [time-out value]
#
#     /var/run/ is scanned for MASQ.* files. If creation time of the file
#     is more than "time-out value" seconds ago the firewall entries
#     listed in the file are deleted by calling /sbin/ipfwadm. The file
#     is deleted, too. If no time-out value is specified 3600s (1h) is
#     used.
#
#     This command should be called periodically by a crontab entry.
#
# c) reject-masq --deleteall
#
#    like b), but delete all firewall entries regardless of time-out value
#
# d) reject-masq -l
#
#    list /proc/net/ip_masquerade and /var/run/MASQ.*
#


#### Start here
umask 0077

# Remove tmp file
rm -rf $FILE_TMP

#set -x 

[ "$DEBUG" != "0" ] && echo "Debug level is $DEBUG"


if [ $[ $DEBUG & 128 ] != 0 ]; then 
    echo "  Using file instead of kernel-proc"
    IPMASQEXEC="cat ./ip_masquerade"
    IPCONEXEC="cat ./ip_localconn"
    IPMASQLOCK="."
else
    IPMASQEXEC="$BIN_NETSTAT --masquerade --numeric -A inet"
    IPCONEXEC="$BIN_NETSTAT --numeric -A inet"
    IPMASQLOCK="/var/run"
fi

## Get local network addresses
# Get local interfaces
cat /proc/net/dev  | grep ':' | awk -F: '{ print $1 }' |  awk '{ print $1 }' | egrep  $NAMEPREFIXIFACE | while read interface rest; do
	if [ $[ $DEBUG & 256 ] != 0 ]; then 
		echo "Found interface: $interface" >&2
	fi
	# Get IP addresses and netmask
	$BIN_IFCONFIG $interface |grep 'inet addr:'| while IFS=": " read tag tag ip tag broad tag mask; do
		if [ "$interface" = "lo" ]; then
			# fix localhost
			mask=$broad
			broad=""
		fi
		if [ $[ $DEBUG & 256 ] != 0 ]; then 
		 	echo -n " IP:$ip Bcast:$broad Mask:$mask" >&2
		fi
		# calculate network
		network=`ipcalc --network $ip $mask | awk -F= '{ print $2 }'`
		if [ $[ $DEBUG & 256 ] != 0 ]; then 
		 	echo " Network:$network" >&2
		fi
		# Cut off suffix
		if [ "$mask" = "255.255.255.0" ]; then
			networkprefix=`echo $network | awk -F. '{ print $1 "." $2 "." $3 "." }'`
		elif [ "$mask" = "255.255.0.0" ]; then
			networkprefix=`echo $network | awk -F. '{ print $1 "." $2 "." }'`
		elif [ "$mask" = "255.0.0.0" ]; then
			networkprefix=`echo $network | awk -F. '{ print $1 "." }'`
		else
			echo -e "\a Unsupported netmask $mask on interface $interface" >&2
			exit 1
		fi
		echo -n " $networkprefix"
	done
done >$FILE_TMP

# Echo networks
if [ $[ $DEBUG & 512 ] != 0 ]; then 
 	echo "Extracted local network prefixes: " >&2
	cat $FILE_TMP >&2
	echo
fi

# Convert extracted local networks to a egrep match rule
LOCALNETPREFIX=`cat $FILE_TMP | sed 's/\./\\\./g' | sed 's/ /|\^/g' | cut -c 2-`

# Echo networks
if [ $[ $DEBUG & 512 ] != 0 ]; then 
 	echo " For match: $LOCALNETPREFIX" >&2
fi


## some functions
usage()
{
	echo " Options:  (see script contents for details)"
	echo "   reject-masq [--insert|--delete [timeout]|--deleteall|--list|-l]"
}

insertFWrules()
{
    date=`date +%s`
    LOCKFILE=$IPMASQLOCK/$IPMASQLOCKPREFIX.$date

	# Delete lockfile, if exists   
	if [ -f $LOCKFILE ]; then 
	    cat /dev/null >$LOCKFILE
	fi

	[ $[ $DEBUG & 4 ] != 0 ] && echo "Test masqueraded connections"

    $IPMASQEXEC | while read proto expire srcip dstip srcprt dummy dstprt dummy; do
		if [ "$proto" = "tcp" -o "$proto" = "udp" -o "$proto" = "icmp" ]; then
			[ $[ $DEBUG & 4 ] != 0 ] && echo " Got: $srcip:$srcprt / $dstip:$dstprt"
	
		    fwruleinfo="forward -p $proto -s $srcip $srcprt -d $dstip $dstprt -i $DIALOUTDEVICE -j REJECT"
	    
		    if [ $[ $DEBUG & 64 ] != 0 ]; then 
				echo "  Exec: $BIN_IPCHAINS -I $fwruleinfo"
    	    else
	    		if [ "$DOLOG" = "1" ]; then
					echo "Insert: $fwruleinfo" >>$FILELOG
		    	fi
	        	$BIN_IPCHAINS -I $fwruleinfo
    	    	echo $fwruleinfo >>$LOCKFILE
    	    fi
		fi
    done

	[ $[ $DEBUG & 4 ] != 0 ] && echo "Test outgoing connections"

	$IPCONEXEC | egrep "^tcp|^udp" | while read proto recvQ sendQ source destination status; do
		if [ "$proto" = "tcp" -o "$proto" = "udp" ]; then
		    srcip=`echo $source | awk -F: '{ print $1 }'`
	    	srcprt=`echo $source | awk -F: '{ print $2 }'`
		    dstip=`echo $destination | awk -F: '{ print $1 }'`
		    dstprt=`echo $destination | awk -F: '{ print $2 }'`
	    
			[ $[ $DEBUG & 4 ] != 0 ] && echo " Test: $srcip:$srcprt / $dstip:$dstprt"

		    if ! echo $srcip | egrep -q $LOCALNETPREFIX ; then
				echo " Get: $srcip:$srcprt / $dstip:$dstprt"
	
				fwruleinfo="output -p $proto -s $srcip $srcprt -d $dstip $dstprt -i $DIALOUTDEVICE -j REJECT"
		
				if [ $[ $DEBUG & 64 ] != 0 ]; then 
				    echo "  Exec: $BIN_IPCHAINS -I $fwruleinfo"
    			else
	    			if [ "$DOLOG" = "1" ]; then
			    		echo "Insert: $fwruleinfo" >>$FILELOG
					fi
	    		    $BIN_IPCHAINS -I $fwruleinfo
    				echo $fwruleinfo >>$LOCKFILE
				fi
    	    fi
		fi
    done
}


## main

if [ $[ $DEBUG & 1 ] != 0 ]; then 
    echo " Option1: $1"	
fi

# insert #
if [ "#$1" = "#--insert" ]; then

    [ $[ $DEBUG & 2 ] != 0 ] && echo "   'insert': start"	

    if [ "$DOLOG" = "1" ]; then
		echo "Triggered: INSERT at `date`" >>$FILELOG
    fi

    insertFWrules
    [ $[ $DEBUG & 2 ] != 0 ] && echo "'insert': done"	
    
# delete #
elif [ "#$1" = "#--delete" ]; then

    [ $[ $DEBUG & 2 ] != 0 ] && echo "   'delete': start"

    if ! [ "$2" = "" ]; then
		TIMEOUT=$2
    fi

    if [ "$DOLOG" = "1" ]; then
		echo "Triggered: DELETE with timeout $TIMEOUT at `date`" >>$FILELOG
    fi

    date=`date +%s`
    
    for i in $IPMASQLOCK/$IPMASQLOCKPREFIX.*; do
	if ! [ "$i" = "$IPMASQLOCK/$IPMASQLOCKPREFIX.*" ]; then
	    fwdate=`echo $i | awk -F. '{ print $NF }'`
	
	    difftime=$[ $date - $fwdate ]
	
#	    echo "Current time: $date, fwrule: $fwdate, diff: $difftime"
	
	    if [ $difftime -ge $TIMEOUT ]; then
    		[ $[ $DEBUG & 2 ] != 0 ] && echo "  Rule has reached timeout"
		
			cat $i | while read line; do

			    if [ $[ $DEBUG & 64 ] != 0 ]; then 
					echo "  Exec: $BIN_IPCHAINS -D $line"
    		    else
					$BIN_IPCHAINS -D $line 2>/dev/null
    		    fi

			    if [ "$?" = "0" ]; then
					if [ "$DOLOG" = "1" ]; then
			    		echo "Remove: $line" >>$FILELOG
					fi
			    fi
	
				done
				rm -f $i
		    fi
		fi
    done


    [ $[ $DEBUG & 2 ] != 0 ] && echo "   'delete': done"

# deleteall #
elif [ "#$1" = "#--deleteall" ]; then
    
    [ $[ $DEBUG & 2 ] != 0 ] && echo "   'deleteall': start"

    if [ "$DOLOG" = "1" ]; then
		echo "Triggered: DELETEALL at `date`" >>$FILELOG
    fi
    
    for i in $IPMASQLOCK/$IPMASQLOCKPREFIX.*; do
		if ! [ "$i" = "$IPMASQLOCK/$IPMASQLOCKPREFIX.*" ]; then
		    cat $i | while read line; do
	    
	        if [ $[ $DEBUG & 64 ] != 0 ]; then 
		    	echo "  Exec: $BIN_IPCHAINS -D $line"
    		else
			    $BIN_IPCHAINS -D $line 2>/dev/null
    	    fi

			if [ "$?" = "0" ]; then
			    if [ "$DOLOG" = "1" ]; then
					echo "Remove: $line" >>$FILELOG
			    fi
			fi
	    	done
		    rm -f $i
		fi
    done
    [ $[ $DEBUG & 2 ] != 0 ] && echo "   'deleteall': done"
    
# command --list
elif [ "#$1" = "#-l" -o "#$1" = "#--list" ]; then
    
    [ $[ $DEBUG & 2 ] != 0 ] && echo "   'list': start"
    
    echo " List current masquerading information"
    
    $IPMASQEXEC
    
    echo " List current installed firewalling rules"
	find $IPMASQLOCK -type f -name "$IPMASQLOCKPREFIX.*" | while read lockfile; do
		echo "Lockfile: $lockfile"
		cat $lockfile
    done
    
    [ $[ $DEBUG & 2 ] != 0 ] && echo "   'list': done"

else
	usage
	exit 1
fi

