#!/usr/bin/bash
#
# log_traffic
#
# Hourly network accounting for Sitestats (analog "net" reports).
# Counts bytes per local IPv4 address via a dedicated nftables table
# (inet bxacct) that sits beside firewalld. Does not use iptables and
# does not touch firewalld policy.
#
# Output format is the historic combined-log fake used by split_logs:
#   <ip> - - [date] "GET /acctin HTTP/1.1" 200 <bytes> "" ""
#

LC_ALL="en_US"
LANG="en_US"
export LC_ALL LANG

LOGFILE="/var/log/ipacct"
STATEDIR="/var/state/acct"
FAMILY="inet"
TABLE="bxacct"
NFT="/usr/sbin/nft"
IPBIN="/usr/sbin/ip"

# Stay off the console. Do not redirect stdout of the whole script:
# list_local_ipv4 is captured via $(...) and dump_chain appends to LOGFILE.
exec 2>/dev/null

if [ ! -x "$NFT" ]; then
    NFT=$(command -v nft) || exit 0
fi
if [ ! -x "$IPBIN" ]; then
    IPBIN=$(command -v ip) || exit 0
fi

# APF owns the packet filter; do not install a parallel hook.
if [ -d /etc/apf ]; then
    exit 0
fi

# OpenVZ often has no usable nft hook.
if [ -e /proc/user_beancounters ]; then
    exit 0
fi

mkdir -p "$STATEDIR"

drop_legacy_iptables() {
    local ipt
    ipt=$(command -v iptables) || return 0
    $ipt -D INPUT -j acctin
    $ipt -D OUTPUT -j acctout
    $ipt -F acctin
    $ipt -F acctout
    $ipt -X acctin
    $ipt -X acctout
}

if [ "$1" = "clean" ]; then
    $NFT delete table "$FAMILY" "$TABLE"
    drop_legacy_iptables
    exit 0
fi

ensure_base() {
    $NFT list table "$FAMILY" "$TABLE" >/dev/null 2>&1 || \
        $NFT add table "$FAMILY" "$TABLE"
    $NFT list chain "$FAMILY" "$TABLE" acctin >/dev/null 2>&1 || \
        $NFT add chain "$FAMILY" "$TABLE" acctin '{ type filter hook input priority -50; policy accept; }'
    $NFT list chain "$FAMILY" "$TABLE" acctout >/dev/null 2>&1 || \
        $NFT add chain "$FAMILY" "$TABLE" acctout '{ type filter hook output priority -50; policy accept; }'
}

list_local_ipv4() {
    $IPBIN -4 -o addr show | awk '{print $4}' | cut -d/ -f1 | grep -E '^[0-9.]+$' | grep -v '^255\.' | grep -v '^0\.' | sort -u
}

dump_chain() {
    local chain="$1"
    local field="$2"
    local uri="$3"
    local date="$4"
    $NFT list chain "$FAMILY" "$TABLE" "$chain" | awk -v date="$date" -v uri="$uri" -v field="$field" '
        $1 == "ip" && $2 == field {
            ip = $3
            bytes = 0
            for (i = 1; i <= NF; i++) {
                if ($i == "bytes") {
                    bytes = $(i + 1)
                    break
                }
            }
            printf "%s - - %s \"GET /%s HTTP/1.1\" 200 %s \"\" \"\"\n", ip, date, uri, bytes
        }
    '
}

ensure_base || exit 0

DATE=$(date +"[%d/%b/%Y:%H:%M:%S %z]")

dump_chain acctin daddr acctin "$DATE" >> "$LOGFILE"
dump_chain acctout saddr acctout "$DATE" >> "$LOGFILE"

# Zero counters by flushing rules, then reinstall one counter per current IP.
$NFT flush chain "$FAMILY" "$TABLE" acctin
$NFT flush chain "$FAMILY" "$TABLE" acctout

for ip in $(list_local_ipv4); do
    $NFT add rule "$FAMILY" "$TABLE" acctin ip daddr "$ip" counter
    $NFT add rule "$FAMILY" "$TABLE" acctout ip saddr "$ip" counter
done

exit 0

#
# Copyright (c) 2008-2026 Michael Stauber, SOLARSPEED.NET
# Copyright (c) 2008-2026 Team BlueOnyx, BLUEONYX.IT
# Copyright (c) 2003 Sun Microsystems, Inc.
# All Rights Reserved.
#
# 1. Redistributions of source code must retain the above copyright
#    notice, this list of conditions and the following disclaimer.
#
# 2. Redistributions in binary form must reproduce the above copyright
#    notice, this list of conditions and the following disclaimer in
#    the documentation and/or other materials provided with the
#    distribution.
#
# 3. Neither the name of the copyright holder nor the names of its
#    contributors may be used to endorse or promote products derived
#    from this software without specific prior written permission.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
# FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
# COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
# BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
# ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
# POSSIBILITY OF SUCH DAMAGE.
#
# You acknowledge that this software is not designed or intended for
# use in the design, construction, operation or maintenance of any
# nuclear facility.
#
