[PATCH] Stream: don't flush empty buffers created for read errors.

Roman Arutyunyan arut at nginx.com
Tue Jun 7 07:01:52 UTC 2022


On Sat, Jun 04, 2022 at 12:13:05AM +0300, Maxim Dounin wrote:
> Hello!
> 
> On Thu, Jun 02, 2022 at 03:20:54AM +0000, Aleksei Bavshin via nginx-devel wrote:
> 
> > > I don't think it's the way to go.  Semantically, flush and
> > > last_buf are mutually exclusive: last_buf implies flushing of all
> > > remaining data, and there should be no flush set if last_buf is
> > > set.
> > > 
> > > The "cl->buf->flush = !src->read->eof" approach might be actually
> > > better and more obvious: it simply ensures that last_buf and flush
> > > are not set at the same time.  I don't immediately see any cases
> > > where it can break things, and this should fix the original
> > > problem with empty UDP packets being sent.
> > 
> > Thanks for confirming that, I wasn't sure it is the right approach exactly because I had doubts regarding the mutual exclusivity of the flags.
> > Updated patch below; tests are passing on both branches and all systems we have in CI.
> > 
> > # HG changeset patch
> > # User Aleksei Bavshin <a.bavshin at f5.com>
> > # Date 1653330584 25200
> > #      Mon May 23 11:29:44 2022 -0700
> > # Node ID 40926829be83986a3a5a5f941d2014000a0acd2e
> > # Parent  e0cfab501dd11fdd23ad492419692269d3a01fc7
> > Stream: don't flush empty buffers created for read errors.
> > 
> > When we generate the last_buf buffer for an UDP upstream recv error, it does
> > not contain any data from the wire. ngx_stream_write_filter attempts to forward
> > it anyways, which is incorrect (e.g., UDP upstream ECONNREFUSED will be
> > translated to an empty packet).
> > 
> > This happens because we mark the buffer as both 'flush' and 'last_buf', and
> > ngx_stream_write_filter has special handling for flush with certain types of
> > connections (see d127837c714f, 32b0ba4855a6). The flags are meant to be
> > mutually exclusive, so the fix is to ensure that flush and last_buf are not set
> > at the same time.
> > 
> > Reproduction:
> > 
> > stream {
> >     upstream unreachable {
> >         server     127.0.0.1:8880;
> >     }
> >     server {
> >         listen     127.0.0.1:8998 udp;
> >         proxy_pass unreachable;
> >     }
> > }
> > 
> > 1 0.000000000    127.0.0.1 → 127.0.0.1    UDP 47 45588 → 8998 Len=5
> > 2 0.000166300    127.0.0.1 → 127.0.0.1    UDP 47 51149 → 8880 Len=5
> > 3 0.000172600    127.0.0.1 → 127.0.0.1    ICMP 75 Destination unreachable (Port
> > unreachable)
> > 4 0.000202400    127.0.0.1 → 127.0.0.1    UDP 42 8998 → 45588 Len=0
> > 
> > Fixes d127837c714f.
> > 
> > diff --git a/src/stream/ngx_stream_proxy_module.c b/src/stream/ngx_stream_proxy_module.c
> > --- a/src/stream/ngx_stream_proxy_module.c
> > +++ b/src/stream/ngx_stream_proxy_module.c
> > @@ -1735,7 +1735,7 @@ ngx_stream_proxy_process(ngx_stream_sess
> >  
> >                  cl->buf->temporary = (n ? 1 : 0);
> >                  cl->buf->last_buf = src->read->eof;
> > -                cl->buf->flush = 1;
> > +                cl->buf->flush = !src->read->eof;
> >  
> >                  (*packets)++;
> >                  *received += n;
> 
> Looks good to me.
> 
> Roman, please take another look.

Good to me too.

This version is equivalent to the original patch for UDP.  The only difference
is a side-effect for TCP.  Now flush is not set in the last buffer.

> 
> -- 
> Maxim Dounin
> http://mdounin.ru/
> _______________________________________________
> nginx-devel mailing list -- nginx-devel at nginx.org
> To unsubscribe send an email to nginx-devel-leave at nginx.org



More information about the nginx-devel mailing list