#!/bin/sh
#
# Uruk init script.
#
# chkconfig: 2345 11 89
# description: starts, stops and saves iptables state, as created by uruk
# beware! above two lines are parsed by chkconfig(8), as commonly found on RPM
# based systems

# Copyright (C) 2002, 2003 Laurence J. Lane
# Copyright (C) 2003, 2004, 2005 Joost van Baal
#
# This file is free software; you can redistribute it and/or modify it under
# the terms of the GNU General Public License.

# Based upon /etc/init.d/iptables as shipped with the Debian iptables
# package by Laurence J. Lane

# this file maintained using arch at http://arch.gna.org/uruk/

set -e

# do sanity check on uruk environment.
enable_uruk_check=true

# enable ipv6 support
enable_ipv6=false

# enable calling the unstable uruk-save script
enable_uruk_save=false

# display a warning before each uruk-save call
enable_uruk_save_warning=true

# set enable_autosave to "true" to autosave the active ruleset
# when going from start to stop
enable_autosave=true

# set enable_save_counters to "true" to save table counters with
# rulesets
enable_save_counters=true

PATH=/usr/local/sbin:/usr/local/bin:/sbin:/bin:/usr/sbin:/usr/bin

# /etc/default/uruk can overrule
# enable_uruk_check, enable_ipv6, enable_autosave, enable_save_counters and PATH
# On Debian systems, configuration for init scripts is in /etc/default/
test -f /etc/default/uruk && . /etc/default/uruk
# On Red Hat systems, configuration for init scripts is in /etc/sysconfig/
test -f /etc/sysconfig/uruk && . /etc/sysconfig/uruk

#
#                        /lib/lsb/init-functions
# Red Hat EL AS rel 2.1        No
# Red Hat EL AS rel 3          Yes
#
# Debian GNU/Linux Sarge       Yes, in lsb-base package
#
# See /usr/share/doc/lsb-core/examples/init-skeleton.gz for sample lsb init
# script.  If we wanna be compliant with Red Hat EL AS rel 2.1, we'd need to
# implement our own log_success_msg, log_failure_msg and log_warning_msg.

initd="$0"

initd_abort () {
  cmd=$1
  shift
  echo "Aborting iptables $cmd: $@."
  exit 0
}

initd_have_a_cow_man () {
  for i in $@; do
    if ! command -v "$i" >/dev/null 2>&1; then
      initd_abort initd "no $i executable"
      exit 0
    fi
  done
}

initd_clear () {
  rm -f "$autosave"
  echo -n "Clearing ${iptables_command} ruleset: default ACCEPT policy"
  $iptables_save | sed "/-/d;/^#/d;s/DROP/ACCEPT/" | $iptables_restore
  echo "."
}

initd_halt () {
  rm -f $autosave
  echo -n "Clearing ${iptables_command} ruleset: default DROP policy"
  $iptables_save | sed "/-/d;/^#/d;s/ACCEPT/DROP/" | $iptables_restore
  echo "."
}

initd_flush () {
  echo -n "Flushing all current $iptables_command rules"
  $iptables_command -F
  echo "."
}

initd_load () {
  ruleset="$libdir/$@"
  if ! test -f "$ruleset"; then
    echo "Can't load ruleset \"$@\": file $ruleset is not present"
    initd_abort load "unknown ruleset \"$@\""
    if ! test "${ruleset#${libdir}/}" = active -o inactive; then
      usage
    fi
    exit 0
  fi
  if test "${ruleset#${libdir}/}" = inactive; then
    initd_autosave
  fi
  rm -f "$autosave"
  ruleset="$libdir/$@"
  echo -n "Loading ${iptables_command} ruleset: load \"$@\""
  $iptables_restore < "$ruleset"
  echo "."
}

initd_counters () {
  if ! test -d "$libdir"; then
    mkdir -p "$libdir"
  fi
  if test "${enable_save_counters:-false}" = true; then
    echo -n " with counters"
    $iptables_save -c > "$ruleset"
  else
    $iptables_save | sed '/^:/s@\[[0-9]\{1,\}:[0-9]\{1,\}\]@[0:0]@g' > "$ruleset"
  fi
}

initd_save () {
  rm -f $autosave
  ruleset="${libdir}/$@"
  echo -n "Saving ${iptables_command} ruleset: save \"$@\""
  initd_counters
  echo "."
}

initd_autosave () {
  if test -f $autosave -a ${enable_autosave-false} = true; then
    ruleset="${libdir}/active"
    echo -n "Autosaving ${iptables_command} ruleset: save \"active\""
    initd_counters
    echo "."
  fi
}

initd_active_uruk_save () {
  warn_uruk_save
  if test $iptables_command = ip6tables; then
    echo -n "Saving IPv6 uruk rules as active ruleset"
    uruk-save -6 > "${libdir}/active"
    echo "."
  else
    echo -n "Saving IPv4 uruk rules as active ruleset"
    uruk-save > "${libdir}/active"
    echo "."
  fi
  initd_load active
}

initd_active () {
  if test $enable_uruk_save = true; then
    initd_active_uruk_save
  else
    initd_flush
    if test $iptables_command = ip6tables; then
      echo -n "Loading IPv6 uruk rules"
      # skip all iptables commands in uruk
      URUK_IPTABLES=':' uruk
      echo "."
    else
      echo -n "Loading IPv4 uruk rules"
      # skip all ip6tables commands in uruk
      URUK_IP6TABLES=':' uruk
      echo "."
    fi
    initd_save active
  fi
}

initd_start () {
  if ! test -s "${libdir}/inactive"; then
    initd_save inactive
  fi
  initd_active
  if test ${enable_autosave-false} = true; then
    touch $autosave
  fi
}

initd_stop () {
  # act sane if inactive state file missing
  ruleset="${libdir}/inactive"
  if test -s $ruleset; then
    initd_load inactive
    rm $ruleset
  else
    echo "Uruk not running (no inactive file found)"
  fi
}

usage () {
cat << END
$initd options:
  start
     If not yet done, save current iptables status in "inactive" ruleset.
     (Re)build and load the "active" ruleset.
  save <ruleset>
     Save the current iptables status in given ruleset.
  create <active|inactive>
     create an "active" or "inactive" ruleset with sane defaults: "active"
     will be based upon the uruk rc file.  "inactive" will allow all traffic.
  load <ruleset>
     load a saved ruleset
  reload
     (Re)build and load the "active" ruleset, without temporarily clearing the
     current iptables status.
  force-reload
     (Re)build and load the "active" ruleset.
  stop
     Load the "inactive" ruleset.
  restart
     Perform stop-actions followed by start-actions.
  clear
     Remove all rules and user-defined chains, set default policy to ACCEPT.
  halt
     Remove all rules and user-defined chains, set default policy to DROP.
  flush
     Flush all rules from the current iptables status.

Saved ruleset locations: /var/lib/uruk/iptables/ and
/var/lib/uruk/ip6tables/ .

END
}

initd_main () {
  case "$1" in
    start)
      initd_start
      ;;
    stop)
      initd_stop
      ;;
    force-reload)
      # FIXME does not behave sane when uruk not running!
      initd_active
      ;;
    restart)
      initd_stop
      initd_start
      ;;
    reload)
      if test $enable_uruk_save = true; then
        # FIXME does not behave sane when uruk not running!
        initd_active_uruk_save
      else
        cat <<END
 Either set enable_uruk_save to true in
 /etc/{default,sysconfig}/uruk or call this script with
 the \"force-reload\" option: cannot reload active file without touching your live
 rules if using uruk-save is disallowed."
END
      fi
      ;;
    clear)
      initd_clear
      ;;
    halt)
      initd_halt
      ;;
    flush)
      initd_flush
      ;;
    save)
      shift
      if test -z "$*"; then
        initd_abort save "no ruleset name given"
      else
        initd_save "$*"
      fi
      ;;
    create)
      shift
      case "$*" in
        active)
          if test $enable_uruk_save = true; then
            warn_uruk_save
            if test $iptables_command = ip6tables; then
              echo -n "Saving IPv6 uruk rules as active ruleset"
              uruk-save -6 > "${libdir}/active"
              echo "."
            else
              echo -n "Saving IPv4 uruk rules as active ruleset"
              uruk-save > "${libdir}/active"
              echo "."
            fi
          else
            cat <<END
 Either set enable_uruk_save to true in
 /etc/{default,sysconfig}/uruk or call this script with
 the \"start\" option: cannot create active file without touching your live
 rules if using uruk-save is disallowed."
END
          fi
          ;;
        inactive)
          initd_clear
          initd_save inactive
          ;;
        *)
          echo "No sane defaults for \"$*\" known"
          ;;
      esac
      ;;
    load)
      shift
      if test -z "$*"; then
        initd_abort load "no ruleset name given"
      else
        initd_load "$*"
      fi
      ;;
    *)
      if test "$iptables_command" = "iptables"; then
        if test -n "$*"; then
          echo "Unknown command: \"$*\""
        fi
        usage
      fi
      ;;
  esac
}

initd_preload() {
  iptables="/sbin/${iptables_command}"
  iptables_save="${iptables}-save"
  iptables_restore="${iptables}-restore"
  uruk_config="/etc/uruk/rc"
  libdir="/var/lib/uruk/${iptables_command}"
  autosave="${libdir}/autosave"
  initd_have_a_cow_man "$iptables_save" "$iptables_restore"
  ${iptables_command} -nL >/dev/null
  initd_main $*
}

check_uruk() {
  initd_have_a_cow_man uruk >/dev/null
  uruk_config="/etc/uruk/rc"
  # check for existence of uruk rc file.
  if ! test -r $uruk_config; then
    echo "No file $uruk_config present."
    exit 1
  fi
  # check for sanity of uruk rc file.
  if grep -q URUK_IS_UNCONFIGURED $uruk_config; then
    echo "Uruk is unconfigured.  Please create a sane file $uruk_config.  See uruk(8)."
    exit 0
  fi
}

warn_uruk_save() {
  if test $enable_uruk_save_warning = true; then
    cat <<END
 About to call the uruk-save script.  You should have read the uruk-rc(5)
 manpage.  Your $uruk_config should be sane.  You should NOT be using any hooks
 in your rc file, see uruk-save(8)!  If you don't like all this, read the "Using
 the Uruk init script" part in the GETTING STARTED section of the uruk(8)
 manpage.
END
  fi
}

if test $enable_uruk_check = true; then
  check_uruk
fi

iptables_command=iptables initd_preload $*
if test $enable_ipv6 = true; then
  iptables_command=ip6tables initd_preload $*
fi

exit 0

