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
>     dera...@arm64.openbsd.org:/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);

Reply via email to