On Thu, 09 Feb 2023 at 02:42:22 +0100, Alexandr Nedvedicky wrote:
> this is my test terminal on remote host:
>     router$ for i in `seq 5` ; do nc 192.168.2.175 22 &  done 
>     [1] 32472
>     [2] 97453
>     [3] 7192
>     [4] 50386
>     [5] 57517
>     router$ SSH-2.0-OpenSSH_9.1
>     SSH-2.0-OpenSSH_9.1
>     SSH-2.0-OpenSSH_9.1
> 
> there are three SSH version strings which indicates
> I got 3 working connection of 5 attempts.

Interesting, I tried with your SSH example and it allowed me to 
connect 5 times.

$ 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

> > 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.

> > @@ -4919,14 +4920,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 &&
> > +                           if (src->state == TCPS_SYN_SENT &&
> >                                 !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);
> > 
> 
>     If I understand things right then in current pf we check conn limit
>     when we see acknowledgment of 3rd client's ACK sent by server. Not sure
>     if I sound clear here so let me draw little diagram:
> 
>       SYN ----> sent by client moves state to
> 
>               TCPS_SYN_SENT : TCPS_LISTEN/TCPS_CLOSED (1)
> 
>       SYN | ACK <---- sent by server moves state to
> 
>               TCPS_SYN_SENT : TCPS_SYN_SENT (2)
> 
>       ACK ----> 3rd ack sent by client move state to:
> 
>               TCPS_ESTABLISHED : TCPS_SYN_SENT (3)
> 
>       ACK <----  server acknowledges client's 3rd ack moves state to
> 
>               TCPS_ESTABLISHED : TCPS_ESTABLISHED (4)
> 
>     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

Reply via email to