> On 11 Jul 2025, at 9:37 AM, Michael Chang via Grub-devel <grub-devel@gnu.org> 
> wrote:
> 
> GRUB's TCP stack assigns source ports for outgoing connections starting
> at 21550 and increments sequentially by 1 (e.g., 21550, 21551, ...).
> While this generally works, it can lead to failures if the system
> reboots rapidly and reuses the same source port too soon.
> 
> This issue was observed on powerpc-ieee1275 platforms using CAS (Client
> Architecture Support) reboot. In such cases, loading the initrd over
> HTTP may fail with connection timeouts. Packet captures show the failed
> connections are flagged as "TCP Port Number Reused" by Wireshark.
> 
> The root cause is that GRUB reuses the same port shortly after reboot,
> while the server may still be tracking the previous connection in
> TIME_WAIT. This can result in the server rejecting the connection
> attempt or responding with a stale ACK or RST, leading to handshake
> failure.
> 
> This patch fixes the issue by introducing a time based source port
> selection strategy. Instead of always starting from port 21550, GRUB now
> computes an initial base port based on the current RTC time, divided
> into 5 minute windows. The purpose of this time based strategy is to
> ensure that GRUB avoids reusing the same source port within a 5 minute
> window, thereby preventing collisions with stale server side connection
> tracking that could interfere with a new TCP handshake.
> 
> A step size of 8 ensures that the same port will not be reused across
> reboots unless GRUB opens more than 8 TCP connections per second on
> average, something that is highly unlikely. In typical usage, a GRUB
> boot cycle lasts about 15 seconds and may open fewer than 100
> connections total, well below the reuse threshold. This makes the
> approach robust against short reboot intervals while keeping the logic
> simple and deterministic.
> 
> Signed-off-by: Michael Chang <mch...@suse.com>
> ---
> grub-core/net/tcp.c | 39 ++++++++++++++++++++++++++++++++++++++-
> 1 file changed, 38 insertions(+), 1 deletion(-)
> 
> diff --git a/grub-core/net/tcp.c b/grub-core/net/tcp.c
> index 93dee0caa..d0589c458 100644
> --- a/grub-core/net/tcp.c
> +++ b/grub-core/net/tcp.c
> @@ -22,6 +22,7 @@
> #include <grub/net/netbuff.h>
> #include <grub/time.h>
> #include <grub/priority_queue.h>
> +#include <grub/datetime.h>
> 
> #define TCP_SYN_RETRANSMISSION_TIMEOUT GRUB_NET_INTERVAL
> #define TCP_SYN_RETRANSMISSION_COUNT GRUB_NET_TRIES
> @@ -552,6 +553,36 @@ grub_net_tcp_accept (grub_net_tcp_socket_t sock,
>   return GRUB_ERR_NONE;
> }
> 
> +/*
> + * Derive a time-based source port to avoid reusing the same port across
> + * reboots. This helps prevent failures caused by server side TCP state (e.g.
> + * TIME_WAIT) from interfering with new connections using the same socket.
> + *
> + * The base port starts at 21550 and increments every second by 8 across a 5
> + * minute window (300 seconds), giving 2400 possible distinct base ports per
> + * window. In typical GRUB usage, the number of connections per boot is 
> small,
> + * so reuse is effectively avoided.
> + */
> +static grub_uint16_t
> +get_initial_base_port (void)
> +{
> +  grub_err_t err;
> +  struct grub_datetime date;
> +  grub_int64_t t = 0;
> +  grub_uint64_t r = 0;
> +
> +  err = grub_get_datetime (&date);
> +  if (err || !grub_datetime2unixtime (&date, &t))

I would suggest to use enum to check err

err != GRUB_ERR_NONE

> +    {
> +      grub_errno = GRUB_ERR_NONE;
> +      return 21550;
> +    }
> +
> +  grub_divmod64 (t, 300, &r);
> +
> +  return 21550 + (r << 3);
> +}
> +
> grub_net_tcp_socket_t
> grub_net_tcp_open (char *server,
>                  grub_uint16_t out_port,
> @@ -569,13 +600,19 @@ grub_net_tcp_open (char *server,
>   struct grub_net_network_level_interface *inf;
>   grub_net_network_level_address_t gateway;
>   grub_net_tcp_socket_t socket;
> -  static grub_uint16_t in_port = 21550;
> +  static grub_uint16_t in_port;
>   struct grub_net_buff *nb;
>   struct tcphdr *tcph;
>   int i;
>   grub_uint8_t *nbd;
>   grub_net_link_level_address_t ll_target_addr;
> 
> +  if (!in_port)
> +    {
> +      in_port = get_initial_base_port ();
> +      grub_dprintf ("net", "base port: %d\n", in_port);
> +    }
> +
>   err = grub_net_resolve_address (server, &addr);
>   if (err)
>     return NULL;
> -- 
> 2.50.0
> 
> 

Reviewed-by: Sudhakar Kuppusamy <sudha...@linux.ibm.com>

> _______________________________________________
> Grub-devel mailing list
> Grub-devel@gnu.org
> https://lists.gnu.org/mailman/listinfo/grub-devel


_______________________________________________
Grub-devel mailing list
Grub-devel@gnu.org
https://lists.gnu.org/mailman/listinfo/grub-devel

Reply via email to