aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMatt Fleming <matt.fleming@intel.com>2013-06-26 10:17:54 +0100
committerMatt Fleming <matt.fleming@intel.com>2013-06-26 10:41:10 +0100
commitc5c37402f45d705cf4fee3870e44d6cd14649971 (patch)
tree8608168b42aeaf095f40257be63cac2c52e454a5
parent042d0e52f7debe5bdf303254e3b8e90d24e97635 (diff)
downloadsyslinux-c5c37402f45d705cf4fee3870e44d6cd14649971.tar.gz
syslinux-c5c37402f45d705cf4fee3870e44d6cd14649971.tar.xz
syslinux-c5c37402f45d705cf4fee3870e44d6cd14649971.zip
efi, udp: use single local port for each connectionsyslinux-6.01-pre2
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@intel.com>
-rw-r--r--core/fs/pxe/pxe.h1
-rw-r--r--efi/udp.c76
2 files changed, 55 insertions, 22 deletions
diff --git a/core/fs/pxe/pxe.h b/core/fs/pxe/pxe.h
index f4b9b6ef..279957ac 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 c762ef38..69706e84 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;