From mboxrd@z Thu Jan 1 00:00:00 1970 Authentication-Results: passt.top; dmarc=pass (p=quarantine dis=none) header.from=redhat.com Authentication-Results: passt.top; dkim=pass (1024-bit key; unprotected) header.d=redhat.com header.i=@redhat.com header.a=rsa-sha256 header.s=mimecast20190719 header.b=LBZr7F7r; dkim-atps=neutral Received: from us-smtp-delivery-124.mimecast.com (us-smtp-delivery-124.mimecast.com [170.10.133.124]) by passt.top (Postfix) with ESMTPS id 439CA5A0623 for ; Thu, 12 Feb 2026 09:59:23 +0100 (CET) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1770886762; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version:content-type:content-type: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=GEJZ6qKQLvcTcjDb+M9gEMtRf8GZFVGCtXtw2j77RVY=; b=LBZr7F7rI9Gb1Qek+3W6ZpmD2M4R24o0qDZD07AJJxXyVkjMKHFyc3lacrQnLgWqCO9VJf H8eq/AAJRrSHOCPV/UuAy1U9AxnqGMvXCfWduNsx9vHYpb0CdAx/MFUk4crv6mZUjxHtrG KE+zCP8ZF+UUPFEhmtC5RSLnHJzoSM4= Received: from mail-wm1-f72.google.com (mail-wm1-f72.google.com [209.85.128.72]) by relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.3, cipher=TLS_AES_256_GCM_SHA384) id us-mta-640-l77z4YvXOluJj4OecBBzhg-1; Thu, 12 Feb 2026 03:59:20 -0500 X-MC-Unique: l77z4YvXOluJj4OecBBzhg-1 X-Mimecast-MFC-AGG-ID: l77z4YvXOluJj4OecBBzhg_1770886759 Received: by mail-wm1-f72.google.com with SMTP id 5b1f17b1804b1-47edf8ba319so29248895e9.2 for ; Thu, 12 Feb 2026 00:59:20 -0800 (PST) X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1770886759; x=1771491559; h=date:content-transfer-encoding:mime-version:organization:references :in-reply-to:message-id:subject:cc:to:from:x-gm-gg :x-gm-message-state:from:to:cc:subject:date:message-id:reply-to; bh=GEJZ6qKQLvcTcjDb+M9gEMtRf8GZFVGCtXtw2j77RVY=; b=r0gO87+63rqt1qZZnnHlGA799Lty/pUvLTw1jccHhQz91P2EVuazAj5QLTM1jJAu10 CKuRyl23eXMxSysDD5qkXVU6ujRTkrvhd9Sl1UNvsurc5qzy+pse8gGxj/zr+EKsEJrV UBmEaqiUooMET3xmFMW2lE1+6Sy2D5xWScShxa5hES5naRND7OJyVUtrO8hCqgvJPZUR NrHsi9f5Fb0rTrJ8MrVTQhqpzueZ4O/r9jQLlfhE7vzcMW0lzSbP45HudfpTWiz8OSA3 ckg8S9njaqxvIV5apsubze6COCSJoduGGQmR3XS4/qqhjV0VYLMHjgytWwZC666lxifg 5Ikw== X-Gm-Message-State: AOJu0YwqkD4g1yOXGxfxWJBNiXtBHQyTysJoIivnGmK5SwFD3FfrkBvR d91PzrK6NtjsZapfagU6VeiU3rS2Ia/kM4i774td6qknIjTucRhX14y/qCxaO6BIDj+qcDz8RaV t7LpkDoaiuFfdOHZ85RW9Kcj0nYLnDtW7oJQ1TaC1UOVotrL8EK8mvQ== X-Gm-Gg: AZuq6aLSCBicPV0jAMId/B+Q08QX8F0hdjIU233ZK7OQtPLOOVg5YjolldVKStHDalp nErIJBNSkiM2pc4q24kb2q2p83FQ/tUKOqW60v35BT5io0e/4c2NaLmaaUqWo7+E1Ou2NmQCt96 Y0CTGdhZCOaPRJWMa2NXCyG4gJ5zanCYfcJSRA6cHtGY5AuErjn2urfkJsFP+w5MxdzqEhm1akI W2pD+7HYskq7sRotqy+27Z5z/hpdx+YA1uPIkariTkc9w/Ylj4lNF9DFe+pNBOfmPE3gEorUL6N 9hipKAFG3Uv3oqa9xc2Be41nd73kC6WbFWg1wlmUvruUfXawESSpRWuYtAzDxY5SwXFvbiQWGE5 NLn+EwoHOqePOK1yTUx1u0I6AJaG9nBIgNQsuIXk7b/Kn5c24zQ== X-Received: by 2002:a05:600c:c83:b0:47e:e87b:af8 with SMTP id 5b1f17b1804b1-48367156119mr20297705e9.21.1770886759088; Thu, 12 Feb 2026 00:59:19 -0800 (PST) X-Received: by 2002:a05:600c:c83:b0:47e:e87b:af8 with SMTP id 5b1f17b1804b1-48367156119mr20297285e9.21.1770886758488; Thu, 12 Feb 2026 00:59:18 -0800 (PST) Received: from maya.myfinge.rs (ifcgrfdd.trafficplex.cloud. [176.103.220.4]) by smtp.gmail.com with ESMTPSA id 5b1f17b1804b1-4835b958b6csm113475315e9.1.2026.02.12.00.59.17 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Thu, 12 Feb 2026 00:59:17 -0800 (PST) From: Stefano Brivio To: Yumei Huang Subject: Re: [PATCH] udp: Split activity timeouts for UDP flows Message-ID: <20260212095916.023a1e80@elisabeth> In-Reply-To: <20260212080414.61889-1-yuhuang@redhat.com> References: <20260212080414.61889-1-yuhuang@redhat.com> Organization: Red Hat X-Mailer: Claws Mail 4.2.0 (GTK 3.24.49; x86_64-pc-linux-gnu) MIME-Version: 1.0 Date: Thu, 12 Feb 2026 09:59:17 +0100 (CET) X-Mimecast-Spam-Score: 0 X-Mimecast-MFC-PROC-ID: t3aYYcaYOOYXebl-JYkqGpvPgn3Z-dZLnlD6ezah8Q8_1770886759 X-Mimecast-Originator: redhat.com Content-Type: text/plain; charset=US-ASCII Content-Transfer-Encoding: 7bit Message-ID-Hash: LORSPU747XEQ3TWR3HA4YZPSMBWLEN3A X-Message-ID-Hash: LORSPU747XEQ3TWR3HA4YZPSMBWLEN3A X-MailFrom: sbrivio@redhat.com X-Mailman-Rule-Misses: dmarc-mitigation; no-senders; approved; emergency; loop; banned-address; member-moderation; nonmember-moderation; administrivia; implicit-dest; max-recipients; max-size; news-moderation; no-subject; digests; suspicious-header CC: passt-dev@passt.top, david@gibson.dropbear.id.au X-Mailman-Version: 3.3.8 Precedence: list List-Id: Development discussion and patches for passt Archived-At: Archived-At: List-Archive: List-Archive: List-Help: List-Owner: List-Post: List-Subscribe: List-Unsubscribe: I haven't tested this yet, just two exceedingly minor nits (I can fix it up on merge if you're fine with it and if no other changes are needed): On Thu, 12 Feb 2026 16:04:14 +0800 Yumei Huang wrote: > Frequent DNS queries over UDP from a container or guest can result > in many sockets shown in ss(8), typically one per flow. This is > expected and harmless, but it can make the output of ss(8) look > noisy and potentially concern users. > > This patch splits UDP flow timeouts into two, mirroring the Linux > kernel, and sources the values from kernel parameters. The shorter > timeout is applied to unidirectional flows and minimal bidirectional > exchanges (single datagram and reply), while the longer timeout is > used for bidirectional flows with multiple datagrams on either side. > > Link: https://bugs.passt.top/show_bug.cgi?id=197 > Suggested-by: Stefano Brivio > Signed-off-by: Yumei Huang > --- > udp.c | 33 ++++++++++++++++++++++++++++++++- > udp.h | 4 ++++ > udp_flow.c | 11 ++++++++--- > udp_flow.h | 13 +++++++++++++ > 4 files changed, 57 insertions(+), 4 deletions(-) > > diff --git a/udp.c b/udp.c > index b2383e2..3afec35 100644 > --- a/udp.c > +++ b/udp.c > @@ -26,7 +26,10 @@ > * > * We track pseudo-connections of this type as flow table entries of type > * FLOW_UDP. We store the time of the last traffic on the flow in uflow->ts, > - * and let the flow expire if there is no traffic for UDP_CONN_TIMEOUT seconds. > + * and let the flow expire if there is no traffic for UDP_TIMEOUT seconds for > + * unidirectional flows and flows with only one datagram and one reply, or > + * UDP_TIMEOUT_STREAM seconds for bidirectional flows with more than one > + * datagram on either side. > * > * NOTE: This won't handle multicast protocols, or some protocols with different > * port usage. We'll need specific logic if we want to handle those. > @@ -118,6 +121,13 @@ > > #define UDP_MAX_FRAMES 32 /* max # of frames to receive at once */ > > +#define UDP_TIMEOUT "/proc/sys/net/netfilter/nf_conntrack_udp_timeout" > +#define UDP_TIMEOUT_STREAM \ > + "/proc/sys/net/netfilter/nf_conntrack_udp_timeout_stream" > + > +#define UDP_TIMEOUT_DEFAULT 30 /* s */ > +#define UDP_TIMEOUT_STREAM_DEFAULT 120 /* s */ > + > /* Maximum UDP data to be returned in ICMP messages */ > #define ICMP4_MAX_DLEN 8 > #define ICMP6_MAX_DLEN (IPV6_MIN_MTU \ > @@ -954,6 +964,7 @@ void udp_sock_handler(const struct ctx *c, union epoll_ref ref, > > flow_trace(uflow, "Received data on reply socket"); > uflow->ts = now->tv_sec; > + udp_flow_activity(uflow, !tosidx.sidei); > > if (pif_is_socket(topif)) { > udp_sock_to_sock(c, ref.fd, n, tosidx); > @@ -1179,6 +1190,24 @@ static void udp_splice_iov_init(void) > } > } > > +/** > + * udp_get_timeout_params() - Get host kernel UDP timeout parameters > + * @c: Execution context > + */ > +static void udp_get_timeout_params(struct ctx *c) > +{ > + intmax_t v; > + > + v = read_file_integer(UDP_TIMEOUT, UDP_TIMEOUT_DEFAULT); > + c->udp.timeout = v; > + > + v = read_file_integer(UDP_TIMEOUT_STREAM, UDP_TIMEOUT_STREAM_DEFAULT); > + c->udp.stream_timeout = v; > + > + debug("Using UDP timeout parameters, timeout: %d, stream_timeout: %d", > + c->udp.timeout, c->udp.stream_timeout); > +} > + > /** > * udp_init() - Initialise per-socket data, and sockets in namespace > * @c: Execution context > @@ -1189,6 +1218,8 @@ int udp_init(struct ctx *c) > { > ASSERT(!c->no_udp); > > + udp_get_timeout_params(c); > + > udp_iov_init(c); > > if (fwd_listen_sync(c, &c->udp.fwd_in, PIF_HOST, IPPROTO_UDP) < 0) > diff --git a/udp.h b/udp.h > index 2b91d72..da9c2df 100644 > --- a/udp.h > +++ b/udp.h > @@ -24,11 +24,15 @@ void udp_update_l2_buf(const unsigned char *eth_d); > * @fwd_in: Port forwarding configuration for inbound packets > * @fwd_out: Port forwarding configuration for outbound packets > * @timer_run: Timestamp of most recent timer run > + * @timeout: Timeout for unidirectional flows (in s) > + * @stream_timeout: Timeout for stream-like flows (in s) > */ > struct udp_ctx { > struct fwd_ports fwd_in; > struct fwd_ports fwd_out; > struct timespec timer_run; > + int timeout; > + int stream_timeout; > }; > > #endif /* UDP_H */ > diff --git a/udp_flow.c b/udp_flow.c > index 1f5e84e..9b22586 100644 > --- a/udp_flow.c > +++ b/udp_flow.c > @@ -17,8 +17,6 @@ > #include "udp_internal.h" > #include "epoll_ctl.h" > > -#define UDP_CONN_TIMEOUT 180 /* s, timeout for ephemeral or local bind */ > - > /** > * udp_at_sidx() - Get UDP specific flow at given sidx > * @sidx: Flow and side to retrieve > @@ -152,6 +150,7 @@ static flow_sidx_t udp_flow_new(const struct ctx *c, union flow *flow, > uflow->ts = now->tv_sec; > uflow->s[INISIDE] = uflow->s[TGTSIDE] = -1; > uflow->ttl[INISIDE] = uflow->ttl[TGTSIDE] = 0; > + uflow->activity[INISIDE] = uflow->activity[TGTSIDE] = 0; > > flow_foreach_sidei(sidei) { > if (pif_is_socket(uflow->f.pif[sidei])) > @@ -362,7 +361,13 @@ bool udp_flow_defer(const struct ctx *c, struct udp_flow *uflow, > bool udp_flow_timer(const struct ctx *c, struct udp_flow *uflow, > const struct timespec *now) > { > - if (now->tv_sec - uflow->ts <= UDP_CONN_TIMEOUT) > + int timeout = c->udp.timeout; > + > + if (uflow->activity[TGTSIDE] && > + (uflow->activity[INISIDE] > 1 || uflow->activity[TGTSIDE] > 1)) > + timeout = c->udp.stream_timeout; > + > + if (now->tv_sec - uflow->ts <= timeout) > return false; > > udp_flow_close(c, uflow); > diff --git a/udp_flow.h b/udp_flow.h > index 14e0f92..158a0f6 100644 > --- a/udp_flow.h > +++ b/udp_flow.h > @@ -16,6 +16,7 @@ > * @flush1: @s[1] may have datagrams queued for other flows > * @ts: Activity timestamp > * @s: Socket fd (or -1) for each side of the flow > + * @activity: Activity for each side of the flow It's not clear what the measurement unit is, here. I think we should also specify that it's packets *coming from* (right?), not going to, each side of the flow. Maybe: * @activity: Packets seen from each side of the flow, up to UINT8_MAX ...if I got it right? > */ > struct udp_flow { > /* Must be first element */ > @@ -29,8 +30,20 @@ struct udp_flow { > > time_t ts; > int s[SIDES]; > + uint8_t activity[SIDES]; > }; > > +/** > + * udp_flow_activity() - Track activity of a udp flow UDP > + * @uflow: UDP flow > + * @sidei: Side index of the flow Maybe we can specify here: (INISIDE or TGTSIDE) > + */ > +static inline void udp_flow_activity(struct udp_flow *uflow, unsigned int sidei) > +{ > + if (uflow->activity[sidei] < UINT8_MAX) > + uflow->activity[sidei]++; > +} > + > struct udp_flow *udp_at_sidx(flow_sidx_t sidx); > flow_sidx_t udp_flow_from_sock(const struct ctx *c, uint8_t pif, > const union inany_addr *dst, in_port_t port, Everything else looks good to me! (But again, I haven't tested this yet). -- Stefano