Hi, The diff is to fix a problem in a complex setup.
Normal setup of divert-reply for TCP connection: client --- relayd --- server - transparently forward TCP connections - divert-reply is configured the outbound connection to the server - so that the PF state is removed when the PCB is deleted - otherwise if packets from server is comming after the PCB is deleted, they are accidentally forwarded directly to the client In addtion to this, "match out nat-to" is configured for the outbound connection instead of dropping "transparent". The purpose of doing this is to expand the space of ephemeral ports of NAT. Ephemeral ports of PCB is limitted in one 2^16 space, but ephemeral ports of PF is limitted in 2^16 for each remote address. In this case, if the PF state is dropped immediately after the PCB is dropped, the port number of NAT might be reused quickly, then a problem can happen on the server side since the port is used for the old connection. So the diff is to keep the state until timeout. comment? Index: sys/net/pf.c =================================================================== RCS file: /cvs/src/sys/net/pf.c,v retrieving revision 1.1138 diff -u -p -r1.1138 pf.c --- sys/net/pf.c 30 Aug 2022 11:53:03 -0000 1.1138 +++ sys/net/pf.c 2 Sep 2022 12:54:36 -0000 @@ -1148,6 +1148,8 @@ pf_find_state(struct pf_pdesc *pd, struc if (s == NULL) return (PF_DROP); + if (ISSET(s->state_flags, PFSTATE_INP_UNLINKED)) + return (PF_DROP); if (s->rule.ptr->pktrate.limit && pd->dir == s->direction) { pf_add_threshold(&s->rule.ptr->pktrate); @@ -1461,7 +1463,23 @@ pf_remove_divert_state(struct pf_state_k if (sk == si->s->key[PF_SK_STACK] && si->s->rule.ptr && (si->s->rule.ptr->divert.type == PF_DIVERT_TO || si->s->rule.ptr->divert.type == PF_DIVERT_REPLY)) { - pf_remove_state(si->s); + if (si->s->key[PF_SK_STACK]->proto == IPPROTO_TCP && + si->s->key[PF_SK_WIRE] != si->s->key[PF_SK_STACK]) { + /* + * If the local address is translated, keep + * the state for "tcp.closed" seconds to + * prevent its source port from being reused. + */ + if (si->s->src.state < TCPS_FIN_WAIT_2 || + si->s->dst.state < TCPS_FIN_WAIT_2) { + pf_set_protostate(si->s, PF_PEER_BOTH, + TCPS_TIME_WAIT); + si->s->timeout = PFTM_TCP_CLOSED; + si->s->expire = getuptime(); + } + si->s->state_flags |= PFSTATE_INP_UNLINKED; + } else + pf_remove_state(si->s); break; } } Index: sys/net/pfvar.h =================================================================== RCS file: /cvs/src/sys/net/pfvar.h,v retrieving revision 1.509 diff -u -p -r1.509 pfvar.h --- sys/net/pfvar.h 20 Jul 2022 09:33:11 -0000 1.509 +++ sys/net/pfvar.h 2 Sep 2022 12:54:37 -0000 @@ -784,6 +784,7 @@ struct pf_state { #define PFSTATE_RANDOMID 0x0080 #define PFSTATE_SCRUB_TCP 0x0100 #define PFSTATE_SETPRIO 0x0200 +#define PFSTATE_INP_UNLINKED 0x0400 #define PFSTATE_SCRUBMASK (PFSTATE_NODF|PFSTATE_RANDOMID|PFSTATE_SCRUB_TCP) #define PFSTATE_SETMASK (PFSTATE_SETTOS|PFSTATE_SETPRIO) u_int8_t log;