ipfw "bug" - recv any = not recv any

Jeff Kletsky jeff+freebsd at wagsky.com
Tue Jul 29 00:25:24 UTC 2008


I hesitate to call this a "bug" as I don't know all the history behind 
the ipfw2 decisions, so let me toss this out there and see I'm just 
missing something.

Overview
========

The negated operator, "not recv any" was taken to mean "any packet never 
received by an interface" believed to be equivalent to "any packet that 
originated on the  current machine's interfaces"

My logic being:
* If "recv any" means "received by any interface"
* then "not recv any" means "not received by any interface" (which 
should be locally-generated packets)

In practice, both "recv any" and "not recv any" appear to be "no-op" 
phrases.



Application was to be able to discriminate between:

* Packets from the current host that are going out
 (and need stateful rules of the form
  my.outside.IP <==> some.rest.of.world.host)

* Packets received from "inside" hosts that had been NAT-ed
 (and need to be let out without another stateful rule, as one was 
created when the packet was received of the form
  my.private.net.IP <==> some.rest.of.world.host)

Agreed, there are ways to solve this in ipfw2 (tagging, for one), but 
the issue is that there is at least one "reasonable" application for the 
phrase and that the behavior is not what one might expect, in a 
potentially dangerous way.

To replicate
============

1) Identify a "blank" rule

[root at wildside /etc/firewall]# ipfw list 20000
ipfw: rule 20000 does not exist

2) create a rule that does not modify traffic, but logs matches, using 
"not recv any"

[root at wildside /etc/firewall]# ipfw add 20000 count all from any to any 
out not recv any
20000 count ip from any to any out

2a) Expect that (2) would have indication that "not recv any" was present

3) delete the rule just created and create another that is identical, 
but without the "not" modifier

[root at wildside /etc/firewall]# ipfw delete 20000
[root at wildside /etc/firewall]# ipfw add 20000 count all from any to any 
out recv any
20000 count ip from any to any out

3a) Note that the generated rule is the same

(enabling logging confirms that both "recv any" and "not recv any" 
appear to be NOOPs)


Discussion
==========

I didn't find much here, but did dig up an older post that looked to be 
discussing this kind of thing

<http://www.mavetju.org/mail/view_message.php?list=freebsd-ipfw&id=971213>

 > From: Patrick O'Reilly <email originally present here>
 > Date: 11 Mar 2001 23:47:44


In my opinion, the following would be "ideal"

1) "recv any" -- matches packets that have been received by the host 
through one of its interfaces
2) "not recv any" -- does not match packets that have been received by 
the host through one of its interfaces

Unfortunately, implementing (1) would likely break a lot of people's 
rule sets

(2), however, I can't immediately see being used without expecting that 
it would fail to match packets that were received by the current host, 
so its implementation would be a bit "safer" for the community



I took a quick look at the code, as Patrick's message suggests matching 
"NULL"

 > Likewise, Rod Grimes suggested:
 > ------------------
 > No, but it should be trivial to patch the code to allow your !any, if
 > you consider that !any is the same as =null:
 > > ipfw count ip from any to any in recv null
 > > Ie, the recv keyword looks at the ifp in the mbuff, the ifp will be 
null
 > for packets originated on the local machine.

/usr/src/sys/netinet/ip_fw2.c -- seemed to be the place, but the start 
of the s/r that caught my eye is not immediately suggesting that there 
is a way to match a null ifp without patching code as its trapped before 
the check is done

static int
iface_match(struct ifnet *ifp, ipfw_insn_if *cmd)
{
       if (ifp == NULL)        /* no iface with this packet, match fails */
               return 0;
       /* Check by name or by IP address */
       if (cmd->name[0] != '\0') { /* match by name */
               /* Check name */
               if (cmd->p.glob) {
                       if (fnmatch(cmd->name, ifp->if_xname, 0) == 0)
                               return(1);
               } else {
                       if (strncmp(ifp->if_xname, cmd->name, IFNAMSIZ) 
== 0)
                               return(1);
               }



Thoughts?

Worth more than just a doc change?

Thanks for the time,

Jeff





More information about the freebsd-security mailing list