On Fri, Jan 07, 2005 at 11:06:18AM -0500, Dave Anderson wrote: > I hadn't realized it until I started thinking this through, but it > appears that, if floating state is enabled, there's no way to prevent > PF from passing *some* spoofed packets -- though this probably isn't a > significant issue in practice. Perhaps the "right" way to deal with > all of these issues is to provide more explicit control over floating > state -- e.g., eliminate the current general floating state and add to > each rule specifying "keep state" (or its relatives) an optional list > of interfaces to which those states may float with a direction for each > such interface.
The state entries contain the direction of the connection precisely to prevent this kind of spoofing. Let's say I allow a connection from A to B in on interface I, by creating a state entry when the first packet of the connection (from A to B) arrives in on I. This state will only allow - incoming packets from A to B - outgoing packets from B to A but not - incoming packets from B to A - outgoing packets from A to B Let's say A is some external host and B is myself, so the firewall is an endpoint of the connection. You clearly don't want to allow any outgoing packets from A to B. Such packets would be spoofed (by the firewall). Neither do you want to allow incoming packets from B to A. Such packets would be spoofed by some external host. The second case is where both A and B are other hosts, i.e. the firewall is filtering a connection that merely passes through, and the firewall is not an endpoint of the connection. In this case, you'll have a second interface J, and see legitimate traffic - incoming packets from A to B on I - outgoing packets from A to B on J - incoming packets from B to A on J - outgoing packets from B to A on I Since a single state entry does not cover all four cases, you can either - filter statefully only on I, passing everything statelessly on J - filter statefully on both I and J, creating TWO state entries for each connection In the second case, you'll get two state entries, passing 1) on I, incoming from A to B, outgoing from B to A 2) on J, outgoing from A to B, incoming from B to A Whether these states are floating or if-bound is completely irrelevant as long as all packets only pass through interfaces I and J. In other words, if you make the first state above floating, that does not mean it will match packets outgoing from A to B or incoming from B to A. You still need a second state for those packets. That's what's surprising to some users. If you make the first state 1) on I, incoming from A to B, outgoing from B to A floating, that merely makes it ignore the interface, becoming 1) on any interface, incoming from A to B, outgoing from B to A Packets outgoing from A to B (or incoming from B to A) on any interface still do not match this state. A floating state is not a 'pass-through' permission for a connection, in the sense that you allow a connection to pass through your firewall on any path possible. You always need a second state for that (unless you pass packets on some interfaces statelessly). The purpose of floating states is setups where packets of one connection may legitimately flow through more than one pair of interfaces, like when you have dynamic routing. For instance, you might have two uplink interfaces I1 and I2. If a connection comes in on I1 (you create a state for a packet from A to B incoming on I1), you might want to allow - incoming packets from A to B - outgoing packets from B to A on I1 and I2, so when one uplink dies (or your routing table changes), the connection initiated through I1 continues to work through I2. Hence, you'd make the state floating (instead of if-bound to I1). Instead of binding a state to a single interface or floating among all interfaces, you can also bind it to a group of interfaces (group-bound), like make I1 and I2 an interface group and bind the state to that group only. But all of this is orthogonal to the fact that you still need a second state if the connection is forwarded THROUGH your firewall and you want to allow that. One state (even if floating) will only cover one side (which might include multiple interfaces), the side facing the first peer of the connection. If you chose to allow the connection to get forwarded to a second peer, you create a second state for that side of the connection (again, possibly through multiple interfaces). This might sound overly complicated. You might want to create a single state entry that allows passage of the connection THROUGH the firewall, not caring for the two sides facing the two peers. But if such a construct would exists, it would suffer precisely the vulnerability against some spoofed packets you mentioned. Because it doesn't exist, the two states can deal with that case. HTH, Daniel