From 4f2b99298a057cea082da632aaecf1b0f490beb9 Mon Sep 17 00:00:00 2001 From: "Costa Tsaousis (ktsaou)" Date: Sun, 25 Jan 2015 17:59:28 +0200 Subject: [PATCH] marks can now be stateful/stateless and temporary/permanent as per #50 --- sbin/firehol.in | 194 ++++++++++++++++++++++++++++++++++++++++-------- 1 file changed, 162 insertions(+), 32 deletions(-) diff --git a/sbin/firehol.in b/sbin/firehol.in index 587e454..537bc8d 100755 --- a/sbin/firehol.in +++ b/sbin/firehol.in @@ -99,18 +99,12 @@ declare -A MARKS_BITS=() declare -A MARKS_MASKS=() declare -A MARKS_MAX=() declare -A MARKS_SHIFT=() +declare -A MARKS_SAVERESTORE=() +declare -A MARKS_STATEFUL=() +MARKS_SAVERESTORE_STATEFUL_MASK="0x00000000" +MARKS_SAVERESTORE_STATELESS_MASK="0x00000000" MARKS_TOTAL_BITS=0 -marksreset() { - #echo >&2 "${FUNCNAME} ${*}: Reseting marks..." - MARKS_BITS=() - MARKS_MASKS=() - MARKS_MAX=() - MARKS_SHIFT=() - MARKS_TOTAL_BITS=0 - #declare -p MARKS_BITS MARKS_MASKS MARKS_MAX MARKS_SHIFT >&2 -} - # taken from http://stackoverflow.com/questions/109023/how-to-count-the-number-of-set-bits-in-a-32-bit-integer # and http://books.google.gr/books?id=iBNKMspIlqEC&pg=PA66&redir_esc=y#v=onepage&q&f=false # counts the number of bits set in a number @@ -135,12 +129,56 @@ ispoweroftwo() { return 1 } +marksreset() { markdef clear; } markdef() { # echo >&2 "${FUNCNAME} ${*}" + if [ "$1" = "reset" -o "$1" = "clear" ] + then + MARKS_BITS=() + MARKS_MASKS=() + MARKS_MAX=() + MARKS_SHIFT=() + MARKS_SAVERESTORE=() + MARKS_STATEFUL=() + MARKS_SAVERESTORE_STATEFUL_MASK="0x00000000" + MARKS_SAVERESTORE_STATELESS_MASK="0x00000000" + MARKS_TOTAL_BITS=0 + return 0 + fi + + local saverestore=1 + local stateful=1 local name="${1}"; shift local max="${1}"; shift + while [ ! -z "${1}" ] + do + case "${1}" in + save|restore|permanent) + local saverestore=1 + ;; + + nosave|norestore|temp|temporary) + local saverestore=0 + ;; + + stateless) + local stateful=0 + ;; + + stateful) + local stateful=1 + ;; + + *) + echo >&2 "${FUNCNAME}: Unknown keyword '${1}'." + exit 1 + ;; + esac + shift + done + if [ ! -z "${MARKS_MASKS[$name]}" ] then echo >&2 "Mark type '${name}' already exists with mask ${MARKS_MASKS[$name]}. Please use 'marksreset' to reset them before re-defining them." @@ -191,9 +229,21 @@ markdef() { MARKS_MAX[$name]=$max MARKS_BITS[$name]=$bits MARKS_MASKS[$name]=$(printf "0x%08x" $mask) + MARKS_STATEFUL[$name]=$stateful + MARKS_SAVERESTORE[$name]=$saverestore + + if [ $saverestore -eq 1 ] + then + if [ $stateful -eq 1 ] + then + MARKS_SAVERESTORE_STATEFUL_MASK=$(printf "0x%08x" $[MARKS_SAVERESTORE_STATEFUL_MASK | mask]) + else + MARKS_SAVERESTORE_STATELESS_MASK=$(printf "0x%08x" $[MARKS_SAVERESTORE_STATELESS_MASK | mask]) + fi + fi # echo "Mark $name with $[MARKS_MAX[$name] + 1] possible values (from 0 to ${MARKS_MAX[$name]}), uses ${MARKS_BITS[$name]} bits, has mask ${MARKS_MASKS[$name]} and values should be shifted by ${MARKS_SHIFT[$name]} bits" - # declare -p MARKS_BITS MARKS_MASKS MARKS_MAX MARKS_SHIFT >&2 + # declare -p MARKS_BITS MARKS_MASKS MARKS_MAX MARKS_SHIFT MARKS_STATEFUL MARKS_SAVERESTORE MARKS_SAVERESTORE_STATEFUL_MASK MARKS_SAVERESTORE_STATELESS_MASK >&2 MARKS_TOTAL_BITS=$[ MARKS_TOTAL_BITS + bits ] } @@ -439,11 +489,60 @@ FIREHOL_TRUST_LOOPBACK=1 # FireHOL allows multiple independent MARKs. # By default FireHOL requires 'connmark' and 'usermark'. -# The possible values supported by each may be defined here. -# The value must be a power of two. -# reset the internal marks to empty - do not remove -marksreset +# Mark types may be defined with this template: +# +# markdef NAME VALUES [stateful|stateless] [permanent|temporary] +# +# NAME = a name for this mark type +# connmark and usermark should always be defined. +# +# VALUES = max number of marks to support (0 to VALUES - 1) +# VALUES must be a power of two. +# +# stateful = all statements that assign this mark should +# only apply it on NEW packets. +# +# stateless = all statements that assign this mark type should +# only apply it only to traffic matched by the +# optional rule parameters given. +# +# temporary = do not save/restore to/from connection marks. +# This means RESPONSES to the matched packets +# will not get the mark. +# +# permanent = save/restore to/from connection marks +# This means that RESPONSES will get the mark. +# +# NOTES ABOUT markdef OPTIONS +# +# default is : stateful permanent +# in this mode, only NEW packets of connections need +# to be marked. ESTABLISHED and RELATED packets +# will automatically get the same mark too. +# So, in FireHOL mark helpers (connmark, mark, custommark) +# you will only need to match a REQUEST packet and +# automatically all the packets of the connection will +# get the mark. +# +# - stateful temporary +# In this mode, only NEW packets will be marked for each +# connection. ESTABLISHED and RELATED packets will NOT +# get the mark. +# +# - stateless permanent +# In this mode, whatever the helper statement matches +# will get the mark. This mark will also be applied to +# all the packets that are encountered after the marked +# packet and are part of the same socket. +# +# - stateless temporary +# In this mode, only whatever the helper statement matches +# will get the mark. Nothing else. +# + +# clear the internal marks - do not remove this line +markdef clear # connmarks are used by the connmark helper markdef connmark 64 @@ -451,11 +550,12 @@ markdef connmark 64 # usermark are used by the mark helper markdef usermark 128 -# Additional types may be defined like this: +# Custom mark example: +# # markdef qosmark 8 -# To use it use 'custommark' helper and match -# The first argument to both is the mark name (qosmark in this case) - +# +# To use it use 'custommark' helper and optional rule parameter. +# The first argument to both should the mark name (qosmark in this case) # ---------------------------------------------------------------------- # IPTABLES PACKETS LOGGING @@ -552,7 +652,7 @@ then fi # save the information for the other tools -declare -p MARKS_BITS MARKS_MASKS MARKS_MAX MARKS_SHIFT >"${FIREHOL_SPOOL_DIR}/marks.conf" +declare -p MARKS_BITS MARKS_MASKS MARKS_MAX MARKS_SHIFT MARKS_STATEFUL MARKS_SAVERESTORE MARKS_SAVERESTORE_STATEFUL_MASK MARKS_SAVERESTORE_STATELESS_MASK >"${FIREHOL_SPOOL_DIR}/marks.conf" # ------------------------------------------------------------------------------ @@ -696,8 +796,8 @@ which_cmd FLOCK_CMD flock if [ ! -f "${FIREHOL_CONFIG_DIR}/firehol-defaults.conf" ] then - "${EGREP_CMD}" "^# --- BEGIN OF FIREHOL DEFAULTS ---" -A 600 "${FIREHOL_FILE}" |\ - "${EGREP_CMD}" "^# --- END OF FIREHOL DEFAULTS ---" -B 600 >"${FIREHOL_CONFIG_DIR}/firehol-defaults.conf" || exit 1 + "${EGREP_CMD}" "^# --- BEGIN OF FIREHOL DEFAULTS ---" -A 1000 "${FIREHOL_FILE}" |\ + "${EGREP_CMD}" "^# --- END OF FIREHOL DEFAULTS ---" -B 1000 >"${FIREHOL_CONFIG_DIR}/firehol-defaults.conf" || exit 1 "${CHOWN_CMD}" root:root "${FIREHOL_CONFIG_DIR}/firehol-defaults.conf" || exit 1 "${CHMOD_CMD}" 600 "${FIREHOL_CONFIG_DIR}/firehol-defaults.conf" || exit 1 fi @@ -3636,12 +3736,23 @@ connmark() { do case "${chain}" in interface) - rule table mangle chain PREROUTING custom '-m conntrack --ctstate NEW' inface "${@}" action MARK to "${mark}" || return 1 - rule table mangle chain POSTROUTING custom '-m conntrack --ctstate NEW' outface "${@}" action MARK to "${mark}" || return 1 + if [ ${MARKS_STATEFUL[connmark]} -eq 1 ] + then + rule table mangle chain PREROUTING custom '-m conntrack --ctstate NEW' inface "${@}" action MARK to "${mark}" || return 1 + rule table mangle chain POSTROUTING custom '-m conntrack --ctstate NEW' outface "${@}" action MARK to "${mark}" || return 1 + else + rule table mangle chain PREROUTING inface "${@}" action MARK to "${mark}" || return 1 + rule table mangle chain POSTROUTING outface "${@}" action MARK to "${mark}" || return 1 + fi ;; *) - rule table mangle chain "${chain}" custom '-m conntrack --ctstate NEW' "${@}" action MARK to "${mark}" || return 1 + if [ ${MARKS_STATEFUL[connmark]} -eq 1 ] + then + rule table mangle chain "${chain}" custom '-m conntrack --ctstate NEW' "${@}" action MARK to "${mark}" || return 1 + else + rule table mangle chain "${chain}" action MARK to "${mark}" || return 1 + fi ;; esac done @@ -3673,7 +3784,12 @@ custommark() { local mark="$(mark_value $name $num)" test -z "${mark}" && work_error=$[work_error + 1] && return 1 - rule table mangle chain "${where}" custom '-m conntrack --ctstate NEW' "${@}" action MARK to "${mark}" || return 1 + if [ ${MARKS_STATEFUL[$name]} -eq 1 ] + then + rule table mangle chain "${where}" custom '-m conntrack --ctstate NEW' "${@}" action MARK to "${mark}" || return 1 + else + rule table mangle chain "${where}" "${@}" action MARK to "${mark}" || return 1 + fi return 0 } @@ -4826,13 +4942,27 @@ close_router() { close_master() { set_work_function "Finilizing firewall policies" - # copy CONNMARK to MARK at the top of mangle, on entry points - iptables_both -t mangle -I OUTPUT 1 -m conntrack --ctstate ESTABLISHED,RELATED -j CONNMARK --restore-mark - iptables_both -t mangle -I PREROUTING 1 -m conntrack --ctstate ESTABLISHED,RELATED -j CONNMARK --restore-mark + if [ ! "${MARKS_SAVERESTORE_STATEFUL_MASK}" = "0x00000000" ] + then + # copy CONNMARK to MARK at the top of mangle, on entry points + iptables_both -t mangle -I OUTPUT 1 -m conntrack --ctstate ESTABLISHED,RELATED -j CONNMARK --restore-mark --mask ${MARKS_SAVERESTORE_STATEFUL_MASK} + iptables_both -t mangle -I PREROUTING 1 -m conntrack --ctstate ESTABLISHED,RELATED -j CONNMARK --restore-mark --mask ${MARKS_SAVERESTORE_STATEFUL_MASK} - # save MARK to CONNMARK at the end of mangle, on exit points - iptables_both -t mangle -A INPUT -m conntrack --ctstate NEW -j CONNMARK --save-mark - iptables_both -t mangle -A POSTROUTING -m conntrack --ctstate NEW -j CONNMARK --save-mark + # save MARK to CONNMARK at the end of mangle, on exit points + iptables_both -t mangle -A INPUT -m conntrack --ctstate NEW -j CONNMARK --save-mark --mask ${MARKS_SAVERESTORE_STATEFUL_MASK} + iptables_both -t mangle -A POSTROUTING -m conntrack --ctstate NEW -j CONNMARK --save-mark --mask ${MARKS_SAVERESTORE_STATEFUL_MASK} + fi + + if [ ! "${MARKS_SAVERESTORE_STATELESS_MASK}" = "0x00000000" ] + then + # copy CONNMARK to MARK at the top of mangle, on entry points + iptables_both -t mangle -I OUTPUT 1 -j CONNMARK --restore-mark --mask ${MARKS_SAVERESTORE_STATELESS_MASK} + iptables_both -t mangle -I PREROUTING 1 -j CONNMARK --restore-mark --mask ${MARKS_SAVERESTORE_STATELESS_MASK} + + # save MARK to CONNMARK at the end of mangle, on exit points + iptables_both -t mangle -A INPUT -j CONNMARK --save-mark --mask ${MARKS_SAVERESTORE_STATELESS_MASK} + iptables_both -t mangle -A POSTROUTING -j CONNMARK --save-mark --mask ${MARKS_SAVERESTORE_STATELESS_MASK} + fi # Accept all related traffic to the established connections rule chain INPUT state RELATED action ACCEPT || return 1