[syslinux:firmware] efi, udp: use single local port for each connection

syslinux-bot for Matt Fleming matt.fleming at intel.com
Wed Jun 26 02:51:05 PDT 2013


Commit-ID:  c5c37402f45d705cf4fee3870e44d6cd14649971
Gitweb:     http://www.syslinux.org/commit/c5c37402f45d705cf4fee3870e44d6cd14649971
Author:     Matt Fleming <matt.fleming at intel.com>
AuthorDate: Wed, 26 Jun 2013 10:17:54 +0100
Committer:  Matt Fleming <matt.fleming at intel.com>
CommitDate: Wed, 26 Jun 2013 10:41:10 +0100

efi, udp: use single local port for each connection

The TFTP protocol uses the local port as an idenitifer during a transfer
(TID), which means that once we've established a TFTP connection, we
must ensure we reuse the same local port number in each packet. Failure
to do so is an error, which causes the TFTP server to send an error
packet.

>From RFC 1350 - THE TFTP PROTOCOL (REVISION 2), Section 4,

 In the next step, and in all succeeding steps, the hosts should make
 sure that the source TID matches the value that was agreed on in
 steps 1 and 2.  If a source TID does not match, the packet should be
 discarded as erroneously sent from somewhere else.  An error packet
 should be sent to the source of the incorrect packet, while not
 disturbing the transfer.

Once the UDPv4 protocol driver has been assigned a local port number
(which happens on the first core_udp_connect()) reuse that number until
core_udp_close() time.

Signed-off-by: Matt Fleming <matt.fleming at intel.com>

---
 core/fs/pxe/pxe.h |  1 +
 efi/udp.c         | 76 +++++++++++++++++++++++++++++++++++++++----------------
 2 files changed, 55 insertions(+), 22 deletions(-)

diff --git a/core/fs/pxe/pxe.h b/core/fs/pxe/pxe.h
index f4b9b6e..279957a 100644
--- a/core/fs/pxe/pxe.h
+++ b/core/fs/pxe/pxe.h
@@ -129,6 +129,7 @@ union net_private {
     } tftp;
     struct net_private_efi {
 	struct efi_binding *binding; /* EFI binding for protocol */
+	uint16_t localport;          /* Local port number (0=not in use) */
     } efi;
 };
 
diff --git a/efi/udp.c b/efi/udp.c
index c762ef3..69706e8 100644
--- a/efi/udp.c
+++ b/efi/udp.c
@@ -24,6 +24,7 @@ static struct efi_binding *udp_reader;
 int core_udp_open(struct pxe_pvt_inode *socket)
 {
     EFI_UDP4_CONFIG_DATA udata;
+    struct efi_binding *b;
     EFI_STATUS status;
     EFI_UDP4 *udp;
 
@@ -33,6 +34,10 @@ int core_udp_open(struct pxe_pvt_inode *socket)
     if (!udp_reader)
 	return -1;
 
+    b = efi_create_binding(&Udp4ServiceBindingProtocol, &Udp4Protocol);
+    if (!b)
+	goto bail;
+
     udp = (EFI_UDP4 *)udp_reader->this;
 
     memset(&udata, 0, sizeof(udata));
@@ -40,13 +45,21 @@ int core_udp_open(struct pxe_pvt_inode *socket)
     udata.AcceptAnyPort = TRUE;
 
     status = uefi_call_wrapper(udp->Configure, 2, udp, &udata);
-    if (status != EFI_SUCCESS) {
-	efi_destroy_binding(udp_reader, &Udp4ServiceBindingProtocol);
-	udp_reader = NULL;
-	return -1;
-    }
+    if (status != EFI_SUCCESS)
+	goto bail;
+
+    socket->net.efi.binding = b;
 
     return 0;
+
+bail:
+    if (b)
+	efi_destroy_binding(b, &Udp4ServiceBindingProtocol);
+
+    efi_destroy_binding(udp_reader, &Udp4ServiceBindingProtocol);
+    udp_reader = NULL;
+
+    return -1;
 }
 
 /**
@@ -56,10 +69,14 @@ int core_udp_open(struct pxe_pvt_inode *socket)
  */
 void core_udp_close(struct pxe_pvt_inode *socket)
 {
-    (void)socket;
-
     efi_destroy_binding(udp_reader, &Udp4ServiceBindingProtocol);
     udp_reader = NULL;
+
+    if (!socket->net.efi.binding)
+	return;
+
+    efi_destroy_binding(socket->net.efi.binding, &Udp4ServiceBindingProtocol);
+    socket->net.efi.binding = NULL;
 }
 
 /**
@@ -73,20 +90,16 @@ void core_udp_connect(struct pxe_pvt_inode *socket, uint32_t ip,
 		      uint16_t port)
 {
     EFI_UDP4_CONFIG_DATA udata;
-    struct efi_binding *b;
     EFI_STATUS status;
     EFI_UDP4 *udp;
 
-    b = efi_create_binding(&Udp4ServiceBindingProtocol, &Udp4Protocol);
-    if (!b)
-	return;
-
-    socket->net.efi.binding = b;
-
-    udp = (EFI_UDP4 *)b->this;
+    udp = (EFI_UDP4 *)socket->net.efi.binding->this;
 
     memset(&udata, 0, sizeof(udata));
 
+    /* Re-use the existing local port number */
+    udata.StationPort = socket->net.efi.localport;
+
     memcpy(&udata.StationAddress, &IPInfo.myip, sizeof(IPInfo.myip));
     memcpy(&udata.SubnetMask, &IPInfo.netmask, sizeof(IPInfo.netmask));
     memcpy(&udata.RemoteAddress, &ip, sizeof(ip));
@@ -94,8 +107,25 @@ void core_udp_connect(struct pxe_pvt_inode *socket, uint32_t ip,
     udata.AcceptPromiscuous = TRUE;
 
     status = uefi_call_wrapper(udp->Configure, 2, udp, &udata);
-    if (status != EFI_SUCCESS)
+    if (status != EFI_SUCCESS) {
 	Print(L"Failed to configure UDP: %d\n", status);
+	return;
+    }
+
+    /*
+     * If this is the first time connecting, save the random local port
+     * number that the UDPv4 Protocol Driver picked for us. The TFTP
+     * protocol uses the local port number as the TID, and it needs to
+     * be consistent across connect()/disconnect() calls.
+     */
+    if (!socket->net.efi.localport) {
+	status = uefi_call_wrapper(udp->GetModeData, 5, udp,
+				   &udata, NULL, NULL, NULL);
+	if (status != EFI_SUCCESS)
+	    Print(L"Failed to get UDP mode data: %d\n", status);
+	else
+	    socket->net.efi.localport = udata.StationPort;
+    }
 }
 
 /**
@@ -105,14 +135,16 @@ void core_udp_connect(struct pxe_pvt_inode *socket, uint32_t ip,
  */
 void core_udp_disconnect(struct pxe_pvt_inode *socket)
 {
-    struct efi_binding *b;
+    EFI_STATUS status;
+    EFI_UDP4 *udp;
 
-    if (!socket->net.efi.binding)
-	return;
+    udp = (EFI_UDP4 *)socket->net.efi.binding->this;
+
+    /* Reset */
+    status = uefi_call_wrapper(udp->Configure, 2, udp, NULL);
+    if (status != EFI_SUCCESS)
+	Print(L"Failed to reset UDP: %d\n", status);
 
-    b = socket->net.efi.binding;
-    efi_destroy_binding(b, &Udp4ServiceBindingProtocol);
-    socket->net.efi.binding = NULL;
 }
 
 static int volatile cb_status = -1;


More information about the Syslinux-commits mailing list