Hi,
There is an awkward behaviour after we have diverted connections
to a socket. When the application removes the socket, the pf state
will persist. A new connection will not hit the divert rule as the
state grabs the packet.
This is bigger issue with connectionless protocols, but can also
happen with TCP if the connection was canceled before the three way
handshake completed.
The solution is to clean up the associated divert state when the
socket gets destroyed. This is possible as both are linked together
and a divert state without socket does not make sense.
Note that this fix does not work for UDP yet as state and socket
are not linked there. I have a diff for that, but it had some
issues.
ok?
bluhm
Index: netinet/in_pcb.c
===================================================================
RCS file: /data/mirror/openbsd/cvs/src/sys/netinet/in_pcb.c,v
retrieving revision 1.146
diff -u -p -u -p -r1.146 in_pcb.c
--- netinet/in_pcb.c 20 Dec 2013 02:04:08 -0000 1.146
+++ netinet/in_pcb.c 12 Jan 2014 12:06:44 -0000
@@ -495,8 +495,21 @@ in_pcbdetach(struct inpcb *inp)
ipsec_delete_policy(inp->inp_ipo);
#endif
#if NPF > 0
- if (inp->inp_pf_sk)
- inp->inp_pf_sk->inp = NULL;
+ if (inp->inp_pf_sk) {
+ struct pf_state_key *sk;
+ struct pf_state_item *si;
+
+ sk = inp->inp_pf_sk;
+ TAILQ_FOREACH(si, &sk->states, entry)
+ if (sk == si->s->key[PF_SK_STACK] && si->s->rule.ptr &&
+ si->s->rule.ptr->divert.port) {
+ pf_unlink_state(si->s);
+ break;
+ }
+ /* pf_unlink_state() may have detached the state */
+ if (inp->inp_pf_sk)
+ inp->inp_pf_sk->inp = NULL;
+ }
#endif
s = splnet();
LIST_REMOVE(inp, inp_lhash);