On Mon, May 18, 2015 at 19:24 +0200, Alexandr Nedvedicky wrote:
> Hello,
>
> during our testing we've discovered small glitch in ICMP state handling.
> we use simple rule as follows:
>
Hi,
> # pfctl -sr
> pass in on vnet2 all flags S/SA
>
If that is the only rule there is, then you need to consider
the default stateless pass. So effectively you've got:
pass all no state
pass in on vnet2 all flags S/SA keep state
> next we create a local outbound traffic using ping to arbitrary destination
> over vnet2 interface. This is what we get:
>
Which is a bad combo really. You're supposed to use a
"pass out keep state" for that, however the behaviour you're
describing is rather peculiar (and reproducible on OpenBSD).
> # ping 172.16.1.2
> PING 172.16.1.2 (172.16.1.2): 56 data bytes
> 64 bytes from 172.16.1.2: icmp_seq=0 ttl=255 time=0.718 ms
> ping: sendto: No route to host
> ping: wrote 172.16.1.2 64 chars, ret=-1
> ping: sendto: No route to host
> ping: wrote 172.16.1.2 64 chars, ret=-1
> </snip>
> 64 bytes from 172.16.1.2: icmp_seq=20 ttl=255 time=0.587 ms
> ping: sendto: No route to host
> ping: wrote 172.16.1.2 64 chars, ret=-1
>
> it looks like state created by icmp_seq=0 response must expire first before
> firewall is able to put next packet to wire.
>
> It took me a while to figure out what's going on here. I think the problem is
> PF keeps packet direction in pf_state::direction, when state gets created,
> while
> the pf_icmp_state_lookup() uses icmp_dir to verify whether packet is valid or
> invalid for given state.
>
> The idea of the fix is straightforward:
>
> remember ICMP direction in pf_pdesc, so it can be passed to newly created
> state for ICMP packet.
>
> the straightforward fix is bit cluttered by change, which switches icmp_dir to
> u_int8_t.
>
This needs a much closer look, but it might be a result of a bad
interaction between stateless and stateful rules. This code is
rather fragile and needs special care.
If you enable debugging (pfctl -x debug) you'll see that the state
is being created for the reply packet (10.10.1.2 is pinging 10.10.1.1):
May 18 20:26:54 pf: key search, if=vmx1:
ICMP wire: (0) 10.10.1.1:8 10.10.1.2:15988
May 18 20:26:54 pf: key search, if=vmx1:
ICMP wire: (0) 10.10.1.1:8 10.10.1.2:15988
May 18 20:26:54 pf: key setup:
ICMP wire: (0) 10.10.1.1:8 10.10.1.2:15988 stack: (0) -
And then the next packet I send matches the state but fails
the direction check that is there to prevent reply spoofing/
replaying:
May 18 20:26:55 pf: key search, if=vmx1:
ICMP wire: (0) 10.10.1.1:8 10.10.1.2:15988
May 18 20:26:55 pf: icmp type 8 in wrong direction (1):
ICMP in wire: (0) 10.10.1.1:8 10.10.1.2:15988 stack: (0) - 0:0 @0
Perhaps we should deny state creation on reply in the first
place, but this might have repercussions of its own. Generally
speaking the advice is to have a proper outgoing state and not
relying on the default "pass all no state" rule. One recommended
way of achieving it is to have a "block all" as your first rule.
> as soon as fix get applied the ping command works, all ICMP probes leave
> firewall host.
>
Thanks for the patch, we'll be investigating this further.
> patch is attached.
>
> regards
> sasha