#!/bin/sh # # firewall Configures Linux firewall services # # # chkconfig: 345 11 89 # description: Configure firewalling and ip forwarding using Linux 2.4's # netfilter. IPTABLES="/sbin/iptables" IP="/sbin/ip" TC="/sbin/tc" ARP="/sbin/arp" CONFIG="/etc/sysconfig/firewall" test -e /etc/rc.d/init.d/functions && { . /etc/rc.d/init.d/functions } || { action() { echo "$1" shift sh -c "$@" } } if_get_addr () { $IP addr show $1 | awk '/inet/ {gsub("/.*", "", $2); print $2}' } if_get_netmask () { $IP addr show $1 | awk '/inet/ {gsub(".*/", "", $2); print $2}' } if_get_broadcast () { $IP addr show $1 | awk '/inet/ {print $4}' } if_get_ether () { $IP link show $1 | awk '/link\/ether/ {print $2}' } addr_is_local () { for addr in $(if_get_addr) $(if_get_broadcast); do if [ "$1" = "$addr" ]; then return 0 fi done return 1 } configure_system () { # # This doesn't autoload: # action "Loading ip_tables module" modprobe ip_tables action "Flushing old firewalling rules" $IPTABLES -F action "Flushing forwarded ports" $IPTABLES -F -t nat if [ ! -f /proc/sys/net/ipv4/ip_forward ] ; then echo "/proc/sys/net/ipv4/ip_forward is missing -- " \ "cannot control IP forwarding" >&2 return 1 fi if [ 1 != "`cat /proc/sys/net/ipv4/ip_forward`" ]; then echo "Routing has not been enabled." >&2 if [ -e /etc/sysctl.conf ] ; then echo "Set net.ipv4.ip_forward and net.ipv4.ip_always_defrag = 1 " >&2 echo " in /etc/sysctl.conf" >&2 else echo "Please set FORWARD_IPV4=\"yes\" in /etc/sysconfig/network" >&2 fi echo " or use your network configuration tool to enable ip forwarding." >&2 return 1 fi # # Set the default for packet forwarding to DROP. We only want to # forward packets for those in our own network. # action "Denying packet forwarding by default" \ $IPTABLES -P FORWARD DROP # # Load all available ip_masq modules. # pushd /lib/modules/`uname -r`/kernel/net/ipv4/netfilter/ >/dev/null action "Loading iptables NAT module" modprobe iptable_nat ls ip_nat* ip_conntrack* | sed 's/.o$//' | while read masqmod ; do action "Loading masquerade module $masqmod " \ modprobe "$masqmod" done popd >/dev/null } common_arg_parse () { while [ -n "$*" ]; do case $1 in tcp) PROTOCOL="--protocol" PROTOCOL_ARG="tcp" ;; udp) PROTOCOL="--protocol" PROTOCOL_ARG="udp" ;; all) # NOP ;; from|host|net) SOURCE="--source" shift SOURCE_ARG="$1" ;; to) DEST="--destination" shift DEST_ARG="$1" if ( addr_is_local $DEST_ARG ) ; then IPTABLES_CHAIN=INPUT else IPTABLES_CHAIN=FORWARD fi ;; port) DEST_PORT="--destination-port" shift DEST_PORT_ARG="$1" ;; connections) STATE="-m state --state NEW" ;; replies) STATE="-m state --state ESTABLISHED,RELATED" ;; on) IN_DEV="--in-interface" shift IN_DEV_ARG="$1" # Only set the chain if the rule isn't "through" # another interface [ -z "$IPTABLES_CHAIN" ] && IPTABLES_CHAIN=INPUT ;; through) OUT_DEV="--out-interface" shift OUT_DEV_ARG="$1" # If the packet passes "through" an interface, # it's always in the FORWARD chain. IPTABLES_CHAIN=FORWARD ;; *) # Save arguments we don't understand for later processing UNCOMMON_ARGS[${#UNCOMMON_ARGS[*]}]="$1" ;; esac shift done IPTABLES_ARGS="$PROTOCOL $PROTOCOL_ARG $SOURCE $SOURCE_ARG $DEST $DEST_ARG $DEST_PORT $DEST_PORT_ARG $IN_DEV $IN_DEV_ARG $OUT_DEV $OUT_DEV_ARG $STATE" # Assume the INPUT chain if no other info is available [ -z "$IPTABLES_CHAIN" ] && IPTABLES_CHAIN=INPUT } common_arg_cleanup () { unset PROTOCOL PROTOCOL_ARG SOURCE SOURCE_ARG DEST DEST_ARG DEST_PORT DEST_PORT_ARG IN_DEV IN_DEV_ARG OUT_DEV OUT_DEV_ARG STATE IPTABLES_ARGS IPTABLES_CHAIN UNCOMMON_ARGS } specific () { case $1 in input) CHAIN=INPUT ;; output) CHAIN=OUTPUT ;; forwarding) CHAIN=FORWARD ;; esac $IPTABLES -P $CHAIN DROP unset CHAIN } deny () { common_arg_parse "$@" $IPTABLES -A $IPTABLES_CHAIN $IPTABLES_ARGS --jump REJECT common_arg_cleanup } allow () { common_arg_parse "$@" $IPTABLES -A $IPTABLES_CHAIN $IPTABLES_ARGS --jump ACCEPT common_arg_cleanup } forward_established_packets () { if [ -z $HAVE_EST_RULE ]; then $IPTABLES -A FORWARD -m state --state ESTABLISHED,RELATED --jump ACCEPT HAVE_EST_RULE=1 fi } masquerade () { common_arg_parse "$@" forward_established_packets $IPTABLES -A FORWARD $IPTABLES_ARGS --jump ACCEPT # Remove the dev args, which aren't usable in the POSTROUTING chain IPTABLES_ARGS="$PROTOCOL $PROTOCOL_ARG $SOURCE $SOURCE_ARG $DEST $DEST_ARG $DEST_PORT $DEST_PORT_ARG" $IPTABLES -t nat -A POSTROUTING $IPTABLES_ARGS --jump MASQUERADE common_arg_cleanup } forward () { if [ "$1" = port ]; then forward_port "$@" else forward_net "$@" fi } forward_net () { common_arg_parse "$@" forward_established_packets $IPTABLES -A FORWARD $IPTABLES_ARGS --jump ACCEPT common_arg_cleanup } forward_port () { ## FIXME: probably needs its own arg parser common_arg_parse "$@" forward_established_packets $IPTABLES -t nat -A PREROUTING $IPTABLES_ARGS --jump DNAT --to-destination $TO_DEST common_arg_cleanup } proxy () { if [ "$1" = arp ]; then proxy_arp "$@" else proxy_ip "$@" fi } proxy_ip () { common_arg_parse "$@" ## FIXME: not yet implimented forward_established_packets $IPTABLES -A PREROUTING -t nat \ -d $LOCAL_IP -j DNAT --to-destination $REMOTE_IP $IPTABLES -A POSTROUTING -t nat \ -s $REMOTE_IP -j SNAT --to-source $LOCAL_IP common_arg_cleanup } proxy_arp () { while [ -n "$*" ]; do case $1 in arp) ;; for) ;; host|net) shift DEST_ARG="$1" ;; on) shift IN_DEV="$1" ;; esac shift done LOCAL_MAC=$(if_get_ether $IN_DEV) $ARP -i $IN_DEV -s $DEST_ARG $LOCAL_MAC pub unset DEST_ARG IN_DEV LOCAL_MAC } flush_arp_proxies () { $ARP -n | awk '{if( $4 = /P/ ) { print "DEST_ARG=" $1 ";IN_DEV=" $5 ; }}' | while read args; do unset DEST_ARG IN_DEV eval $args $ARP -i $IN_DEV -d $DEST_ARG pub done unset DEST_ARG IN_DEV } throttle_add_root_to () { OUT_DEV=$1 if eval [ -z '$'${OUT_DEV}'_HAS_ROOT' ]; then $TC qdisc add dev ${OUT_DEV} root handle 1: htb eval ${OUT_DEV}'_HAS_ROOT'=1 eval ${OUT_DEV}'_SEQ'=0 fi } throttle () { common_arg_parse "$@" if [ -z "$OUT_DEV_ARG" ]; then echo Throttle rules require an output device. echo Make sure you specify which device this traffic passes \"through\". common_arg_cleanup return fi throttle_add_root_to $OUT_DEV_ARG SEQ=$(( ${OUT_DEV_ARG}_SEQ + 10 )) $TC class add dev $OUT_DEV_ARG parent 1: classid 1:$SEQ htb ${UNCOMMON_ARGS[*]} $TC qdisc add dev $OUT_DEV_ARG parent 1:$SEQ handle $SEQ: sfq perturb 10 # Remove the dev args, which aren't usable in the POSTROUTING chain IPTABLES_ARGS="$PROTOCOL $PROTOCOL_ARG $SOURCE $SOURCE_ARG $DEST $DEST_ARG $DEST_PORT $DEST_PORT_ARG $STATE" iptables -A POSTROUTING -t mangle $IPTABLES_ARGS --jump MARK --set-mark $SEQ $TC filter add dev $OUT_DEV_ARG parent 1:0 protocol ip handle $SEQ fw flowid 1:$SEQ eval ${OUT_DEV_ARG}'_SEQ'=$SEQ common_arg_cleanup } flush_throttle_controls () { /sbin/tc qdisc | awk '/1: / {print $5}' | while read $DEV_WITH_QDISK ; do $TC qdisc del dev eth0 root done } #----------------------- if [ ! -f $CONFIG ] ; then echo Couldn\'t find config file $CONFIG. ## FIXME: suggest documentation's location. exit fi case "$1" in start) configure_system . $CONFIG ;; stop) $IPTABLES -F $IPTABLES -P FORWARD ACCEPT $IPTABLES -F -t nat $IPTABLES -F -t mangle flush_arp_proxies flush_throttle_controls ;; status) echo "Current firewalling rules are:" ; $IPTABLES -L echo ; $IPTABLES -L -t nat ;; restart|reload) $0 stop $0 start ;; *) echo "Usage: firewall {start|stop|restart|reload|status}" exit 1 ;; esac