public inbox for passt-dev@passt.top
 help / color / mirror / code / Atom feed
* [PATCH 0/2] Fix handling of ICMP flows with 0 id
@ 2024-12-05  4:26 David Gibson
  2024-12-05  4:26 ` [PATCH 1/2] udp: Improve detail of UDP endpoint sanity checking David Gibson
                   ` (2 more replies)
  0 siblings, 3 replies; 4+ messages in thread
From: David Gibson @ 2024-12-05  4:26 UTC (permalink / raw)
  To: Stefano Brivio, passt-dev; +Cc: steffhip, David Gibson

Well, that's the impetus, but it's done as a more general changing of
where we sanity check flow endpoints.

Link: https://bugs.passt.top/show_bug.cgi?id=105

David Gibson (2):
  udp: Improve detail of UDP endpoint sanity checking
  flow: Remove over-zealous sanity checks in flow_sidx_hash()

 flow.c     |  7 +------
 udp_flow.c | 32 ++++++++++++++++++++++++--------
 2 files changed, 25 insertions(+), 14 deletions(-)

-- 
2.47.0


^ permalink raw reply	[flat|nested] 4+ messages in thread

* [PATCH 1/2] udp: Improve detail of UDP endpoint sanity checking
  2024-12-05  4:26 [PATCH 0/2] Fix handling of ICMP flows with 0 id David Gibson
@ 2024-12-05  4:26 ` David Gibson
  2024-12-05  4:26 ` [PATCH 2/2] flow: Remove over-zealous sanity checks in flow_sidx_hash() David Gibson
  2024-12-05 21:46 ` [PATCH 0/2] Fix handling of ICMP flows with 0 id Stefano Brivio
  2 siblings, 0 replies; 4+ messages in thread
From: David Gibson @ 2024-12-05  4:26 UTC (permalink / raw)
  To: Stefano Brivio, passt-dev; +Cc: steffhip, David Gibson

In udp_flow_new() we reject a flow if the endpoint isn't unicast, or it has
a zero endpoint port.  Those conditions aren't strictly illegal, but we
can't safely handle them at present:
 * Multicast UDP endpoints are certainly possible, but our current flow
   tracking only makes sense for simple unicast flows - we'll need
   different handling if we want to handle multicast flows in future
 * It's not entirely clear if port 0 is RFC-ishly correct, but for socket
   interfaces port 0 sometimes has a special meaning such as "pick the port
   for me, kernel".  That makes flows on port 0 unsafe to forward in the
   usual way.

For the same reason we also can't safely handle port 0 as our port.  In
principle that's also true for our address, however in the case of flows
initiated from a socket, we may not know our address since the socket
could be bound to 0.0.0.0 or ::, so we can only verify that our address
is unicast for flows initiated from the tap side.

Refine the current check in udp_flow_new() to slightly more detailed checks
in udp_flow_from_sock() and udp_flow_from_tap() to make what is and isn't
handled clearer.  This makes this checking more similar to what we do for
TCP connections.

Signed-off-by: David Gibson <david@gibson.dropbear.id.au>
---
 udp_flow.c | 32 ++++++++++++++++++++++++--------
 1 file changed, 24 insertions(+), 8 deletions(-)

diff --git a/udp_flow.c b/udp_flow.c
index b81be2c..c8fdb5f 100644
--- a/udp_flow.c
+++ b/udp_flow.c
@@ -75,16 +75,10 @@ void udp_flow_close(const struct ctx *c, struct udp_flow *uflow)
 static flow_sidx_t udp_flow_new(const struct ctx *c, union flow *flow,
 				int s_ini, const struct timespec *now)
 {
-	const struct flowside *ini = &flow->f.side[INISIDE];
 	struct udp_flow *uflow = NULL;
 	const struct flowside *tgt;
 	uint8_t tgtpif;
 
-	if (!inany_is_unicast(&ini->eaddr) || ini->eport == 0) {
-		flow_trace(flow, "Invalid endpoint to initiate UDP flow");
-		goto cancel;
-	}
-
 	if (!(tgt = flow_target(c, flow, IPPROTO_UDP)))
 		goto cancel;
 	tgtpif = flow->f.pif[TGTSIDE];
@@ -189,6 +183,7 @@ flow_sidx_t udp_flow_from_sock(const struct ctx *c, union epoll_ref ref,
 			       const union sockaddr_inany *s_in,
 			       const struct timespec *now)
 {
+	const struct flowside *ini;
 	struct udp_flow *uflow;
 	union flow *flow;
 	flow_sidx_t sidx;
@@ -210,7 +205,19 @@ flow_sidx_t udp_flow_from_sock(const struct ctx *c, union epoll_ref ref,
 		return FLOW_SIDX_NONE;
 	}
 
-	flow_initiate_sa(flow, ref.udp.pif, s_in, ref.udp.port);
+	ini = flow_initiate_sa(flow, ref.udp.pif, s_in, ref.udp.port);
+
+	if (!inany_is_unicast(&ini->eaddr) ||
+	    ini->eport == 0 || ini->oport == 0) {
+		/* In principle ini->oddr also must be unicast, but when we've
+		 * been initiated from a socket bound to 0.0.0.0 or ::, we don't
+		 * know our address, so we have to leave it unpopulated.
+		 */
+		flow_err(flow, "Invalid endpoint on UDP recvfrom()");
+		flow_alloc_cancel(flow);
+		return FLOW_SIDX_NONE;
+	}
+
 	return udp_flow_new(c, flow, ref.fd, now);
 }
 
@@ -233,6 +240,7 @@ flow_sidx_t udp_flow_from_tap(const struct ctx *c,
 			      in_port_t srcport, in_port_t dstport,
 			      const struct timespec *now)
 {
+	const struct flowside *ini;
 	struct udp_flow *uflow;
 	union flow *flow;
 	flow_sidx_t sidx;
@@ -256,7 +264,15 @@ flow_sidx_t udp_flow_from_tap(const struct ctx *c,
 		return FLOW_SIDX_NONE;
 	}
 
-	flow_initiate_af(flow, PIF_TAP, af, saddr, srcport, daddr, dstport);
+	ini = flow_initiate_af(flow, PIF_TAP, af, saddr, srcport,
+			       daddr, dstport);
+
+	if (!inany_is_unicast(&ini->eaddr) || ini->eport == 0 ||
+	    !inany_is_unicast(&ini->oaddr) || ini->oport == 0) {
+		flow_dbg(flow, "Invalid endpoint on UDP packet");
+		flow_alloc_cancel(flow);
+		return FLOW_SIDX_NONE;
+	}
 
 	return udp_flow_new(c, flow, -1, now);
 }
-- 
@@ -75,16 +75,10 @@ void udp_flow_close(const struct ctx *c, struct udp_flow *uflow)
 static flow_sidx_t udp_flow_new(const struct ctx *c, union flow *flow,
 				int s_ini, const struct timespec *now)
 {
-	const struct flowside *ini = &flow->f.side[INISIDE];
 	struct udp_flow *uflow = NULL;
 	const struct flowside *tgt;
 	uint8_t tgtpif;
 
-	if (!inany_is_unicast(&ini->eaddr) || ini->eport == 0) {
-		flow_trace(flow, "Invalid endpoint to initiate UDP flow");
-		goto cancel;
-	}
-
 	if (!(tgt = flow_target(c, flow, IPPROTO_UDP)))
 		goto cancel;
 	tgtpif = flow->f.pif[TGTSIDE];
@@ -189,6 +183,7 @@ flow_sidx_t udp_flow_from_sock(const struct ctx *c, union epoll_ref ref,
 			       const union sockaddr_inany *s_in,
 			       const struct timespec *now)
 {
+	const struct flowside *ini;
 	struct udp_flow *uflow;
 	union flow *flow;
 	flow_sidx_t sidx;
@@ -210,7 +205,19 @@ flow_sidx_t udp_flow_from_sock(const struct ctx *c, union epoll_ref ref,
 		return FLOW_SIDX_NONE;
 	}
 
-	flow_initiate_sa(flow, ref.udp.pif, s_in, ref.udp.port);
+	ini = flow_initiate_sa(flow, ref.udp.pif, s_in, ref.udp.port);
+
+	if (!inany_is_unicast(&ini->eaddr) ||
+	    ini->eport == 0 || ini->oport == 0) {
+		/* In principle ini->oddr also must be unicast, but when we've
+		 * been initiated from a socket bound to 0.0.0.0 or ::, we don't
+		 * know our address, so we have to leave it unpopulated.
+		 */
+		flow_err(flow, "Invalid endpoint on UDP recvfrom()");
+		flow_alloc_cancel(flow);
+		return FLOW_SIDX_NONE;
+	}
+
 	return udp_flow_new(c, flow, ref.fd, now);
 }
 
@@ -233,6 +240,7 @@ flow_sidx_t udp_flow_from_tap(const struct ctx *c,
 			      in_port_t srcport, in_port_t dstport,
 			      const struct timespec *now)
 {
+	const struct flowside *ini;
 	struct udp_flow *uflow;
 	union flow *flow;
 	flow_sidx_t sidx;
@@ -256,7 +264,15 @@ flow_sidx_t udp_flow_from_tap(const struct ctx *c,
 		return FLOW_SIDX_NONE;
 	}
 
-	flow_initiate_af(flow, PIF_TAP, af, saddr, srcport, daddr, dstport);
+	ini = flow_initiate_af(flow, PIF_TAP, af, saddr, srcport,
+			       daddr, dstport);
+
+	if (!inany_is_unicast(&ini->eaddr) || ini->eport == 0 ||
+	    !inany_is_unicast(&ini->oaddr) || ini->oport == 0) {
+		flow_dbg(flow, "Invalid endpoint on UDP packet");
+		flow_alloc_cancel(flow);
+		return FLOW_SIDX_NONE;
+	}
 
 	return udp_flow_new(c, flow, -1, now);
 }
-- 
2.47.0


^ permalink raw reply related	[flat|nested] 4+ messages in thread

* [PATCH 2/2] flow: Remove over-zealous sanity checks in flow_sidx_hash()
  2024-12-05  4:26 [PATCH 0/2] Fix handling of ICMP flows with 0 id David Gibson
  2024-12-05  4:26 ` [PATCH 1/2] udp: Improve detail of UDP endpoint sanity checking David Gibson
@ 2024-12-05  4:26 ` David Gibson
  2024-12-05 21:46 ` [PATCH 0/2] Fix handling of ICMP flows with 0 id Stefano Brivio
  2 siblings, 0 replies; 4+ messages in thread
From: David Gibson @ 2024-12-05  4:26 UTC (permalink / raw)
  To: Stefano Brivio, passt-dev; +Cc: steffhip, David Gibson

In flow_sidx_hash() we verify that the flow we're hashing doesn't have an
unspecified endpoint address, or zero for either port.  The hash table only
works if we're looking for exact matches of address and port, and this is
attempting to catch any cases where we might have left address or port
unpopulated or filled with a wildcard.

This doesn't really work though, because there are cases where unspecified
addresses or zero ports are correct:
 * We already use unspecified addresses for our address in cases where we
   don't know the specific local address for that side, and exclude the
   obvious extra check on side->oaddr for that reason.
 * Zero port numbers aren't strictly forbidden over the wire.  We forbid
   them for TCP & UDP because they can't safely be handled on the socket
   side.  However for ICMP a zero id, which goes in the port field is
   valid.
 * Possible future flow types (for example, for multicast protocols) might
   legitimately have an unspecified address.

Although it makes them easier to miss, these sorts of sanity checks really
have to be done at the protocol / flow type layer, and we already do so.
Remove the checks in flow_sidx_hash() other than checking that the pif
is specified.

Link: https://bugs.passt.top/show_bug.cgi?id=105

Signed-off-by: David Gibson <david@gibson.dropbear.id.au>
---
 flow.c | 7 +------
 1 file changed, 1 insertion(+), 6 deletions(-)

diff --git a/flow.c b/flow.c
index 1ea112b..ee1221b 100644
--- a/flow.c
+++ b/flow.c
@@ -597,12 +597,7 @@ static uint64_t flow_sidx_hash(const struct ctx *c, flow_sidx_t sidx)
 	const struct flowside *side = &f->side[sidx.sidei];
 	uint8_t pif = f->pif[sidx.sidei];
 
-	/* For the hash table to work, entries must have complete endpoint
-	 * information, and at least a forwarding port.
-	 */
-	ASSERT(pif != PIF_NONE && !inany_is_unspecified(&side->eaddr) &&
-	       side->eport != 0 && side->oport != 0);
-
+	ASSERT(pif != PIF_NONE);
 	return flow_hash(c, FLOW_PROTO(f), pif, side);
 }
 
-- 
@@ -597,12 +597,7 @@ static uint64_t flow_sidx_hash(const struct ctx *c, flow_sidx_t sidx)
 	const struct flowside *side = &f->side[sidx.sidei];
 	uint8_t pif = f->pif[sidx.sidei];
 
-	/* For the hash table to work, entries must have complete endpoint
-	 * information, and at least a forwarding port.
-	 */
-	ASSERT(pif != PIF_NONE && !inany_is_unspecified(&side->eaddr) &&
-	       side->eport != 0 && side->oport != 0);
-
+	ASSERT(pif != PIF_NONE);
 	return flow_hash(c, FLOW_PROTO(f), pif, side);
 }
 
-- 
2.47.0


^ permalink raw reply related	[flat|nested] 4+ messages in thread

* Re: [PATCH 0/2] Fix handling of ICMP flows with 0 id
  2024-12-05  4:26 [PATCH 0/2] Fix handling of ICMP flows with 0 id David Gibson
  2024-12-05  4:26 ` [PATCH 1/2] udp: Improve detail of UDP endpoint sanity checking David Gibson
  2024-12-05  4:26 ` [PATCH 2/2] flow: Remove over-zealous sanity checks in flow_sidx_hash() David Gibson
@ 2024-12-05 21:46 ` Stefano Brivio
  2 siblings, 0 replies; 4+ messages in thread
From: Stefano Brivio @ 2024-12-05 21:46 UTC (permalink / raw)
  To: David Gibson; +Cc: passt-dev, steffhip

On Thu,  5 Dec 2024 15:26:00 +1100
David Gibson <david@gibson.dropbear.id.au> wrote:

> Well, that's the impetus, but it's done as a more general changing of
> where we sanity check flow endpoints.
> 
> Link: https://bugs.passt.top/show_bug.cgi?id=105
> 
> David Gibson (2):
>   udp: Improve detail of UDP endpoint sanity checking
>   flow: Remove over-zealous sanity checks in flow_sidx_hash()

Applied.

-- 
Stefano


^ permalink raw reply	[flat|nested] 4+ messages in thread

end of thread, other threads:[~2024-12-05 21:46 UTC | newest]

Thread overview: 4+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2024-12-05  4:26 [PATCH 0/2] Fix handling of ICMP flows with 0 id David Gibson
2024-12-05  4:26 ` [PATCH 1/2] udp: Improve detail of UDP endpoint sanity checking David Gibson
2024-12-05  4:26 ` [PATCH 2/2] flow: Remove over-zealous sanity checks in flow_sidx_hash() David Gibson
2024-12-05 21:46 ` [PATCH 0/2] Fix handling of ICMP flows with 0 id Stefano Brivio

Code repositories for project(s) associated with this public inbox

	https://passt.top/passt

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for IMAP folder(s).