Hello,
On Wed, Feb 08, 2023 at 09:42:11PM -0600, joshua stein wrote:
</snip>
> $ for i in `seq 5` ; do nc 192.168.1.240 22 & done
> [2] 68892
> [3] 6303
> [4] 63554
> [5] 87833
> [6] 49997
> $ SSH-2.0-OpenSSH_9.1
> SSH-2.0-OpenSSH_9.1
> SSH-2.0-OpenSSH_9.1
> SSH-2.0-OpenSSH_9.1
> SSH-2.0-OpenSSH_9.1
>
> vm:~$ doas pfctl -sr
> block return all
> pass out all flags S/SA
> pass in on egress inet6 proto tcp from any to ::1 port = 22 flags S/SA keep
> state (source-track rule, max-src-conn 3)
> pass in on egress inet proto tcp from any to 127.0.0.1 port = 22 flags S/SA
> keep state (source-track rule, max-src-conn 3)
> pass in on egress inet proto tcp from any to 192.168.1.240 port = 22 flags
> S/SA keep state (source-track rule, max-src-conn 3)
>
> This is with:
>
> OpenBSD 7.2-current (GENERIC.MP) #2014: Tue Feb 7 16:24:04 MST 2023
> [email protected]:/usr/src/sys/arch/arm64/compile/GENERIC.MP
I gave it a try after doing a sysupgrade to:
penBSD 7.2-current (GENERIC.MP) #1025: Wed Feb 8 19:16:09 MST 2023
it still works for me as expected:
disk$ for i in `seq 5` ; do nc 192.168.2.175 22 & done
[1] 51566
[2] 78983
[3] 77864
[4] 37474
[5] 98599
disk$ SSH-2.0-OpenSSH_9.2
SSH-2.0-OpenSSH_9.2
SSH-2.0-OpenSSH_9.2
my connection arrives over iwn0 interface which is in egress group
so our environments are almost identical.
</snip>
>
> > > diff --git sys/net/pf.c sys/net/pf.c
> > > index 8cb1326a160..89703feab12 100644
> > > --- sys/net/pf.c
> > > +++ sys/net/pf.c
> > > @@ -481,12 +481,10 @@ pf_src_connlimit(struct pf_state **stp)
> > > if ((sn = pf_get_src_node((*stp), PF_SN_NONE)) == NULL)
> > > return (0);
> > >
> > > - sn->conn++;
> > > - (*stp)->src.tcp_est = 1;
> > > pf_add_threshold(&sn->conn_rate);
> > >
> > > if ((*stp)->rule.ptr->max_src_conn &&
> > > - (*stp)->rule.ptr->max_src_conn < sn->conn) {
> > > + sn->conn >= (*stp)->rule.ptr->max_src_conn) {
> > > pf_status.lcounters[LCNT_SRCCONN]++;
> > > bad++;
> > > }
> > > @@ -497,8 +495,11 @@ pf_src_connlimit(struct pf_state **stp)
> > > bad++;
> > > }
> > >
> > > - if (!bad)
> > > + if (!bad) {
> > > + sn->conn++;
> > > + (*stp)->src.tcp_est = 1;
> > > return (0);
> > > + }
> > >
> > > if ((*stp)->rule.ptr->overload_tbl) {
> > > struct pfr_addr p;
> >
> > it seems to me the change to pf_src_connlimit() does
> > not alter behavior. I think change to pf_src_connlimit()
> > can be dropped.
>
> But don't we not want to increment the source node's connection
> count since we're not going to accept the connection (in the !bad
> case)? I'm not sure what kind of bookkeeping that would screw up.
>
what we currently do is we always bump connection count
for source node we found. then we are going to check limit
(*stp)->rule.ptr->max_src_conn < sn->conn
if the limit is exceeded we mark as closed and expired (timeout
PFTM_PURGE). We also report that to caller which should close the
connection.
your change stops counting connections as soon as limit is
reached. So now I see there is a change in behavior. I've missed
that yesterday. I'm not able to tell if we want go that way or not.
</snip>
> > currently we do conn limit check in step (4). Your change moves this
> > to earlier step (3) (given I understand things right here).
> > It's awfully early here I need sleep on this.
>
> Yes, that was my understanding too. We wait until the remote has
> done enough work to be a valid connection but then block it before
> sending the final ack.
>
> > can you give it a try with your slightly modified diff? just drop
> > changes to pf_src_connlimit() and keep those in pf_tcp_track_full() which
> > I believe is the only relevant part.
>
> Yes, it still works, and only allows me 3 connections with the final
> 2 timing out as expected:
>
> $ for i in `seq 5` ; do nc 192.168.1.240 22 & done
> [2] 10193
> [3] 30197
> [4] 72235
> [5] 69900
> [6] 99044
> $ SSH-2.0-OpenSSH_9.1
> SSH-2.0-OpenSSH_9.1
> SSH-2.0-OpenSSH_9.1
> [5] - exit 1 nc 192.168.1.240 22
> $
> [6] + exit 1 nc 192.168.1.240 22
>
the only explanation why it does not work for you is latency. The packets which
match state run as a readers. so if we call pf_set_protostate() before we
actually check the limit then it might be the cause here. I admit it's
rather a speculation.
can you give a try to diff below?
thanks and
regards
sashan
--------8<---------------8<---------------8<------------------8<--------
diff --git a/sys/net/pf.c b/sys/net/pf.c
index 8cb1326a160..f81b0c793ce 100644
--- a/sys/net/pf.c
+++ b/sys/net/pf.c
@@ -4919,14 +4919,14 @@ pf_tcp_track_full(struct pf_pdesc *pd, struct pf_state
**stp, u_short *reason,
pf_set_protostate(*stp, psrc, TCPS_CLOSING);
if (th->th_flags & TH_ACK) {
if (dst->state == TCPS_SYN_SENT) {
- pf_set_protostate(*stp, pdst,
- TCPS_ESTABLISHED);
if (src->state == TCPS_ESTABLISHED &&
!SLIST_EMPTY(&(*stp)->src_nodes) &&
pf_src_connlimit(stp)) {
REASON_SET(reason, PFRES_SRCLIMIT);
return (PF_DROP);
}
+ pf_set_protostate(*stp, pdst,
+ TCPS_ESTABLISHED);
} else if (dst->state == TCPS_CLOSING)
pf_set_protostate(*stp, pdst,
TCPS_FIN_WAIT_2);