IPFW dst-ip any == not dst-ip any ?

Given the following experiment:

Bash:
# ipfw add 10 count dst-ip any
00010 count

# ipfw add 20 count not dst-ip any
00020 count

# ipfw show 1-100
00010 41 12728 count
00020 40 12624 count

Why do both rules appear the same in the list? I'm perfectly ok with the first rule being optimized down to simply "count", because "dst-ip any" predicate is always true and can be dropped. But then, I would expect that "not dst-ip any" is always false, that is it never matches any ip. So why does that second rule count anything? It looks like it counts the same packets as the first rule (small discrepancy 41-40 is due to stray traffic between those two commands).
 
Bug. Somebody should fix that, but nobody cares, because these rules have no practical use.
This one works better:

Code:
root@dzhn:~ # ipfw add 10 count dst-ip 0.0.0.0/0
00010 count
root@dzhn:~ # ipfw add 20 count not dst-ip 0.0.0.0/0
ipfw: not any never matches

But then:

Code:
root@dzhn:~ # ipfw add 20 count not dst-ip6 ::/0
ipfw: not any never matches
root@dzhn:~ # ipfw add 10 count dst-ip6 ::/0
00010 count dst-ip6 me6

Ups. That one's seriously wrong...

For hand-coded rulesets just don't use these.
For software-created (where 'any' could be a legal outcome of the compute) just leaving it out doesn't work, because no conditions at all will not be accepted:

Code:
root@dzhn:~ # ipfw add 20 count
ipfw: missing protocol

So we need to give it some protocol instead. Example from my code (the result in 's' can then safely be negated):

Code:
    s = " #{dir}-ip"
    case netobj.typ
    when Netobj::NET
      if netobj.all?
        s = " proto ip"
        s << "4" if netobj.val.addr.ipv4?
      end
      s << "6" if netobj.val.addr.ipv6?
      s << " " + netobj.val.addr.to_scidr unless netobj.all?
    [...]
    end
 
But then, I would expect that "not dst-ip any" is always false, that is it never matches any ip.
I'm guessing the interpretation is more like (not dst-ip) == any, in other words (src-ip or src-ip6 or dst-ip6 ) == any, not matching any IP would probably be dst-ip not any but that makes no sense, what would be the inverse of any IP? No IP? Then it's not a valid IP packet.
 
/*
* The first set of opcodes compares the packet's
* fields with some pattern, setting 'match' if a
* match is found. At the end of the loop there is
* logic to deal with F_NOT and F_OR flags associated
* with the opcode.
*/

C:
            if (cmd->len & F_NOT)
                match = !match;

 
SirDice , when used in rule options, "not" inverts the result of matching the following predicate. So, not dst-ip xxxx is not (dst-ip xxxx), as VladiBG has shown. At least, this is how it works with specific IPs: not dst-ip 192.168.0.1 will match every IP except that one.
dst-ip not any isn't valid syntax.
what would be the inverse of any IP? No IP? Then it's not a valid IP packet.
Right! This is exactly what I have expected: predicate not dst-ip any should never match any valid IP packet. Practically useless since it is, such rule could be completely eliminated by the firewall instruction compiler, see "not any never matches" messages in PMc 's post.
 
What you are trying to achieve? Do you try to count no IP protocol frames?
There's example in the man page of ipfw (PACKET FLOW section) for filtering on layer2 and bridge but i recomment not to enable it as it will become very confusing when you first need to match and allow L2 based on MAC address and after that to process regular IP_input/IP_output it's not worth it.
 
What you are trying to achieve?
I'm just exploring ipfw to see what it can and what it can't do. "count" action was chosen for test purposes only, because it doesn't interfer with the rest of the working firewall rules.

This particular experiment was to test if there's any way to switch the given rule off by apending some specially crafted predicate to it. I wanted the following two code snippets to give the same result:

Code:
# This one obviously works...
if [ condition ]; then
    /sbin/ipfw add any_rule_I_might_want_here
fi

Code:
# ... and i wonder if it can be rewritten like this:
if [ condition ]; then
    ENABLE_IF_CONDITION=""
else
    ENABLE_IF_CONDITION="some magic predicate that would make this rule never match"
fi

/sbin/ipfw add any_rule_I_might_want_here ${ENABLE_IF_CONDITION}

I found several solutions to this experiment, for example:

Code:
ENABLE_IF_CONDITION="tagged <some_tag_that_is_never_used>"

But while experimenting with "not any" approach, I've discovered that strange inconsistent behaviour and posted this question. It has no practical goal apart from figuring out whether that behaviour is legit, or was it a bug.
 
  • Thanks
Reactions: PMc
It's better to use tables for this and not modify the rules. It's very common use in fail2ban for example. It monitor the logs for faulty logins or other abnormal attempts and dynamically add and remove the intruder ip address into a predefined table in IPFW which is blocked.

00550 deny tcp from table(1) to any

This way the rule and rule number stay the same and you don't have to take care of removing the rule. The only modification is happening only in the content of the table which holds the blocked ip addresses.
 
Back
Top