From mboxrd@z Thu Jan 1 00:00:00 1970 Received: from mail-io1-f68.google.com (mail-io1-f68.google.com [209.85.166.68]) by mx.groups.io with SMTP id smtpd.web10.20540.1590176836023843262 for ; Fri, 22 May 2020 12:47:16 -0700 Authentication-Results: mx.groups.io; dkim=pass header.i=@gmail.com header.s=20161025 header.b=STXifv12; spf=pass (domain: gmail.com, ip: 209.85.166.68, mailfrom: andrey.warkentin@gmail.com) Received: by mail-io1-f68.google.com with SMTP id k18so12655304ion.0 for ; Fri, 22 May 2020 12:47:15 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20161025; h=from:to:cc:subject:date:message-id; bh=/9Zy7asFYUOsFOPljnME3TaH5mxBo6fjnF0UN35M7Fw=; b=STXifv12PkYl6x10JzVJ7VtfocuAskEHHs3WRdDPHAE+1jrp8L1SzXGmd8JrM8HxJS xLMnYFpTZoUMGDNyi2nipLfAM/lqQNl4AAcntGWMgqB7q/aT+AoHNfY5g+ZZEHr9VVLK msavRAIbhIWMFOe5TaTERnwou3OayTBL2aLLTOBVbPN3Rd2QKtLT5l2cuw5gffrtHql1 OY7DpSULMANOS6mUTEhAJnBoDeA9BX6fJ8XTDXsZjWR3A1/7tNJRotZ3lRg9DvRip4yh MatAuIdutvLI4vW/alS/AsIwNfAy43G7VzYC3KmNnkDq8DZTRjpqLEt25Sq+jLYb5Hsa IAKg== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:from:to:cc:subject:date:message-id; bh=/9Zy7asFYUOsFOPljnME3TaH5mxBo6fjnF0UN35M7Fw=; b=N8Ej/fteJuAMyvOlHzHYCzJSuywJvReHQdH+ZU/xznGnfcm4py97Nukaep4446QVgi cv5FQc0FGXl1wJveM9G+LdkTQeCuQLmPQnSqkf8spiwYZLEwdbdp+89iJC19bw6H+3XJ pbl6+cH63xgq4Nah4DjADhHMSgxF8TNDFv7kvhdMbPshGMpsyLN/rtPVAOneMfqeqrSz FjfJDiUdXWw4rXRqBgRFK8MTRsAY2I3n7m3OHB4c15ZiOwD9WMK3TLF6VfHBGXNHBlSz LJG4gw4fT+grXQ2rnltCatdjnsYHy2R1hRzYWZkeOLf353frLiy3T9T0yQCe7S78yrD6 kGQg== X-Gm-Message-State: AOAM532QoBfbyXd54zQprfcW/V3R/UY3RZaf/c1gy6P5IngMi599TAMx RBcMuVBZw5jxiQLTNKaQDPi7PH4oCJM= X-Google-Smtp-Source: ABdhPJz0nGYplJxadzy3FThUpD9rpMx47YPLn20OXiXPfB0CMjznqJGurbodZ2UXMu+MQlgLCDzjoQ== X-Received: by 2002:a05:6638:5a2:: with SMTP id b2mr9912572jar.59.1590176834892; Fri, 22 May 2020 12:47:14 -0700 (PDT) Return-Path: Received: from localhost.localdomain (c-98-214-99-181.hsd1.il.comcast.net. [98.214.99.181]) by smtp.gmail.com with ESMTPSA id w88sm5247651ilk.83.2020.05.22.12.47.14 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Fri, 22 May 2020 12:47:14 -0700 (PDT) From: "Andrei Warkentin" To: devel@edk2.groups.io Cc: maciej.rabeda@linux.intel.com, jiaxin.wu@intel.com, siyuan.fu@intel.com Subject: [edk2][PATCH 1/1] NetworkPkg/HttpBootDxe: handle servers which may FIN after file sizing in HttpBootLoadFile Date: Fri, 22 May 2020 12:46:59 -0700 Message-Id: <20200522194659.54423-1-andrey.warkentin@gmail.com> X-Mailer: git-send-email 2.17.1 This is v2 with Maciej Rabeda's feedback. Python http.server seems to FIN after the first HEAD request to size the loaded file is completed and ACKed. What happens next is interesting. On low latency connections, the GET request to download may get sent after the server sends the FIN but before the client has a chance to process it. The net result is: - Server ignores GET - HttpBootLoadFile returns EFI_CONNECTION_FIN. Boot fails. In the other case, client handles the FIN before attempting the GET, so there's a proper three-way handshake as part of GET. The solution is to retry HttpBootLoadFile 2 times if it returns EFI_CONNECTION_FIN. This is because HttpBootLoadFile may issue up to 3 requests: HEAD/GET to get size and final GET to load. Some servers may send a FIN after each request. The first request (HEAD) is not supposed to fail this way, so only the two subsequent GET request may result in a FIN. Fixes https://bugzilla.tianocore.org/show_bug.cgi?id=2720 This what the boot looks like now (when using that Python server): >>Start HTTP Boot over IPv4.... Station IP address is 10.0.1.186 URI: http://10.0.1.57/bootaa64.efi Error: Server has terminated the connection. URI: http://10.0.1.57/bootaa64.efi File Size: 179160 Bytes Downloading...100% Cc: Maciej Rabeda Cc: Jiaxin Wu Cc: Siyuan Fu Signed-off-by: Andrei Warkentin --- NetworkPkg/HttpBootDxe/HttpBootImpl.c | 35 +++++++++++++++++++- NetworkPkg/HttpDxe/HttpImpl.c | 5 +++ 2 files changed, 39 insertions(+), 1 deletion(-) diff --git a/NetworkPkg/HttpBootDxe/HttpBootImpl.c b/NetworkPkg/HttpBootDxe/HttpBootImpl.c index 4a51f35cdd..1a251b4347 100644 --- a/NetworkPkg/HttpBootDxe/HttpBootImpl.c +++ b/NetworkPkg/HttpBootDxe/HttpBootImpl.c @@ -288,6 +288,7 @@ HttpBootDhcp ( @retval EFI_NOT_STARTED The driver is in stopped state. @retval EFI_BUFFER_TOO_SMALL The BufferSize is too small to read the boot file. BufferSize has been updated with the size needed to complete the request. + @retval EFI_CONNECTION_FIN Server had closed the connection while we were waiting downloading. @retval EFI_DEVICE_ERROR An unexpected network error occurred. @retval Others Other errors as indicated. @@ -408,6 +409,8 @@ ON_EXIT: AsciiPrint ("\n Error: Server response timeout.\n"); } else if (Status == EFI_ABORTED) { AsciiPrint ("\n Error: Remote boot cancelled.\n"); + } else if (Status == EFI_CONNECTION_FIN) { + AsciiPrint ("\n Error: Server has terminated the connection.\n"); } else if (Status != EFI_BUFFER_TOO_SMALL) { AsciiPrint ("\n Error: Unexpected network error.\n"); } @@ -553,6 +556,7 @@ HttpBootDxeLoadFile ( BOOLEAN UsingIpv6; EFI_STATUS Status; HTTP_BOOT_IMAGE_TYPE ImageType; + UINTN MaxTries; if (This == NULL || BufferSize == NULL || FilePath == NULL) { return EFI_INVALID_PARAMETER; @@ -598,13 +602,42 @@ HttpBootDxeLoadFile ( // Load the boot file. // ImageType = ImageTypeMax; - Status = HttpBootLoadFile (Private, BufferSize, Buffer, &ImageType); + // + // HttpBootLoadFile may issue up to 2 requests: HEAD/GET to get + // size and final GET to load. Some servers may send a FIN after + // each request. The first request (HEAD) is not supposed to + // fail this way, so only the two possible GETs need the special + // handling. + // + MaxTries = 2; + do { + Status = HttpBootLoadFile (Private, BufferSize, Buffer, &ImageType); + if (Status == EFI_CONNECTION_FIN) { + if (Private->HttpCreated) { + // + // Tear down HTTP/TCP state entirely. Http->Configure (NULL) is not + // sufficient (EFI_ACCESS_DENIED from TCP stack on subsequent + // HttpBootLoadFile. + // + HttpIoDestroyIo (&Private->HttpIo); + Private->HttpCreated = FALSE; + } + } + } while (MaxTries-- && Status == EFI_CONNECTION_FIN); + if (EFI_ERROR (Status)) { if (Status == EFI_BUFFER_TOO_SMALL && (ImageType == ImageTypeVirtualCd || ImageType == ImageTypeVirtualDisk)) { Status = EFI_WARN_FILE_SYSTEM; } else if (Status != EFI_BUFFER_TOO_SMALL) { HttpBootStop (Private); } + if (Status == EFI_CONNECTION_FIN) { + // + // EFI_CONNECTION_FIN is not an expected error for EFI_LOAD_FILE_PROTOCOL.LoadFile(), so + // map it to closest matching error. Note: already logged the error in HttpBootLoadFile. + // + Status = EFI_ABORTED; + } return Status; } diff --git a/NetworkPkg/HttpDxe/HttpImpl.c b/NetworkPkg/HttpDxe/HttpImpl.c index 5a6ecbc9d9..34a33b09f7 100644 --- a/NetworkPkg/HttpDxe/HttpImpl.c +++ b/NetworkPkg/HttpDxe/HttpImpl.c @@ -959,6 +959,8 @@ HttpBodyParserCallback ( @retval EFI_OUT_OF_RESOURCES Failed to complete the operation due to lack of resources. @retval EFI_NOT_READY Can't find a corresponding Tx4Token/Tx6Token or the EFI_HTTP_UTILITIES_PROTOCOL is not available. + @retval EFI_CONNECTION_FIN Server had closed the connection while we were waiting for + a response. **/ EFI_STATUS @@ -1528,6 +1530,9 @@ Error: @retval EFI_OUT_OF_RESOURCES Could not allocate enough system resources. @retval EFI_ACCESS_DENIED An open TCP connection is not present with the host specified by response URL. + + @retval EFI_CONNECTION_FIN Server had closed the connection while we were waiting for + a response. **/ EFI_STATUS EFIAPI -- 2.17.1