Skip site navigation (1)Skip section navigation (2)
Date:      Sun, 13 Jul 2025 23:41:26 +0300
From:      Christos Chatzaras <chris@cretaforce.gr>
To:        freebsd-net <freebsd-net@freebsd.org>
Cc:        FreeBSD Questions Mailing List <freebsd-questions@freebsd.org>
Subject:   Issues with IPFW skipto Rule and Whitelisting Logic 
Message-ID:  <3A01EF48-EBE8-48C3-9C66-6A250A240341@cretaforce.gr>

next in thread | raw e-mail | index | archive | help
I am using ipfw with these rules:

----------------
#!/bin/sh

# Set rules command prefix
cmd=3D"ipfw -q add "
cmd2=3D"ipfw -q "

# Public interface
pif=3D`ifconfig -l | awk '{ print $1 }'`

# Flush all rules
ipfw -q -f flush

# Flush all tables
$cmd2 table 1 flush
$cmd2 table 3 flush

# Allow loopback and deny loopback spoofing
$cmd 00010 allow ip from any to any via lo0
$cmd 00020 deny ip from any to 127.0.0.0/8
$cmd 00030 deny ip from 127.0.0.0/8 to any

# Catch spoofing from outside.
$cmd 00031 deny ip from any to any not antispoof via $pif

# Checks stateful rules
$cmd 00050 check-state
$cmd 00060 deny tcp from any to any established

# ALLOW WHITELIST - IGNORE RULE 00100
$cmd2 00070 add skipto 00101 ip from 'table(3)' to any

# DENY INCOMING LIST
$cmd 00100 reset ip from 'table(1)' to any

# ICMP
$cmd 01010 allow icmp from any to any out via $pif keep-state
$cmd 01011 allow icmp from any to any in via $pif

# WWW
$cmd 10031 allow tcp from me to any dst-port 443 out via $pif setup =
keep-state
$cmd 10033 allow tcp from any to me dst-port 443 in via $pif setup =
keep-state

# Deny everything else, and log it
$cmd 56599 deny log all from any to any
----------------

And ipfw list includes:

----------------
00070 skipto 101 ip from table(3) to any
00100 reset ip from table(1) to any
----------------

Currently, table(1) holds about 1.9 million entries (both individual IPs =
and subnets), while table(3) contains about 10,000 entries (also a mix =
of single IPs and subnets).

These tables are populated using this script few times per day:

----------------
#!/bin/sh

tempdir=3D$(mktemp -d /tmp/ipfw.XXXXXX)
trap "rm -rf $tempdir" EXIT

fetch -q -o "$tempdir/allow.txt" https://example.com/ipfw/allow.txt || =
exit 1
fetch -q -o "$tempdir/deny.txt" https://example.com/ipfw/deny.txt || =
exit 1

update_table() {
    table=3D$1
    file=3D$2
    current_file=3D"$tempdir/current_table_$table.txt"
    ipfw -q table "$table" list | awk '{print $1}' | sed 's/\/32$//' | =
sort > "$current_file"
    cat "$file" | sed 's/\/32$//' | sort | uniq > =
"$tempdir/new_table_$table.txt"

    comm -13 "$tempdir/new_table_$table.txt" "$current_file" | while =
read -r ip; do
        [ -n "$ip" ] && ipfw -q table "$table" delete "$ip"
    done

    comm -23 "$tempdir/new_table_$table.txt" "$current_file" | while =
read -r ip; do
        [ -n "$ip" ] && ipfw -q table "$table" add "$ip"
    done
}

update_table 3 "$tempdir/allow.txt"
update_table 1 "$tempdir/deny.txt"
----------------

My intended logic is that any IP present in table(3) should always be =
allowed, even if it or its subnet also appears in table(1).

For instance, 175.178.167.241 is in table(3), while 175.178.0.0/16 is =
present in table(1).

After rebooting the server and populating the tables by running the =
update script for the first time, access from 175.178.167.241 works =
correctly. However, after subsequent runs of the update script - which =
only updates unrelated entries and does not modify 175.178.167.241 or =
175.178.0.0/16 - access from 175.178.167.241 is no longer permitted.

Additionally, when this issue arises, adding 175.178.0.0/16 to table(3) =
allows access again. Even after removing that entry, as long as =
175.178.167.241 remains in table(3) and I wait for any active sessions =
to clear, access continues to work.

Does anyone have any ideas about what could be causing this behavior?=



Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?3A01EF48-EBE8-48C3-9C66-6A250A240341>