aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorGene Cumm <gene.cumm@gmail.com>2013-09-23 17:13:53 -0400
committerGene Cumm <gene.cumm@gmail.com>2013-09-23 17:13:53 -0400
commit131b9981b8dbf5fa1c014995f44b10b7b099fe12 (patch)
tree8f660bac2786b551872e46c1450a40c0029361da
parentceb14335790f70378dddfbf0202d2bcda0c54a03 (diff)
downloadsyslinux-131b9981b8dbf5fa1c014995f44b10b7b099fe12.tar.gz
syslinux-131b9981b8dbf5fa1c014995f44b10b7b099fe12.tar.xz
syslinux-131b9981b8dbf5fa1c014995f44b10b7b099fe12.zip
PXELINUX: Use sendto() instead of connect()/send()/disconnect()
This commit prevents a race-condition on systems that have functional interrupts (observed with iPXE and select other Dell systems). Without this, the reply packet could be received by the core prior to the disconnect() call, see that it doesn't have a matching PCB (protocol control block, iirc) since the reply has a different far-end UDP port than the original request, and lwIP will discard the packet before PXELINUX can see it. net_core_sendto() instead of net_core_connect() net_core_send() net_core_disconnect() Commit message expanded with Matt Fleming's assistance Signed-off-by: Gene Cumm <gene.cumm@gmail.com>
-rw-r--r--core/fs/pxe/core.c48
-rw-r--r--core/fs/pxe/tftp.c14
-rw-r--r--core/include/net.h3
-rw-r--r--core/legacynet/core.c37
4 files changed, 94 insertions, 8 deletions
diff --git a/core/fs/pxe/core.c b/core/fs/pxe/core.c
index e330ba82..c1de8957 100644
--- a/core/fs/pxe/core.c
+++ b/core/fs/pxe/core.c
@@ -6,6 +6,8 @@
#include <net.h>
#include "pxe.h"
+#include <dprintf.h>
+
const struct url_scheme url_schemes[] = {
{ "tftp", tftp_open, 0 },
{ "http", http_open, O_DIRECTORY },
@@ -81,6 +83,7 @@ void net_core_connect(struct pxe_pvt_inode *socket, uint32_t ip,
struct net_private_lwip *priv = &socket->net.lwip;
struct ip_addr addr;
+ dprintf2("net_core_connect: %08X %04X\n", ntohl(ip), port);
addr.addr = ip;
netconn_connect(priv->conn, &addr, port);
}
@@ -174,6 +177,51 @@ out:
netbuf_delete(nbuf);
}
+ /**
+ * Send a UDP packet to a destination
+ *
+ * @param:socket, the open socket
+ * @param:data, data buffer to send
+ * @param:len, size of data bufer
+ * @param:ip, the ip address
+ * @param:port, the port number, host-byte order
+ */
+void net_core_sendto(struct pxe_pvt_inode *socket, const void *data,
+ size_t len, uint32_t ip, uint16_t port)
+{
+ struct netconn *conn = socket->net.lwip.conn;
+ struct ip_addr addr;
+ struct netbuf *nbuf;
+ void *pbuf;
+ int err;
+
+ nbuf = netbuf_new();
+ if (!nbuf) {
+ printf("netbuf allocation error\n");
+ return;
+ }
+
+ pbuf = netbuf_alloc(nbuf, len);
+ if (!pbuf) {
+ printf("pbuf allocation error\n");
+ goto out;
+ }
+
+ memcpy(pbuf, data, len);
+
+ dprintf("net_core_sendto: %08X %04X\n", ntohl(ip), port);
+ addr.addr = ip;
+
+ err = netconn_sendto(conn, nbuf, &addr, port);
+ if (err) {
+ printf("netconn_sendto error %d\n", err);
+ goto out;
+ }
+
+out:
+ netbuf_delete(nbuf);
+}
+
/**
* Network stack-specific initialization
*/
diff --git a/core/fs/pxe/tftp.c b/core/fs/pxe/tftp.c
index 9b755c93..c04e0f55 100644
--- a/core/fs/pxe/tftp.c
+++ b/core/fs/pxe/tftp.c
@@ -255,18 +255,16 @@ void tftp_open(struct url_info *url, int flags, struct inode *inode,
timeout_ptr = TimeoutTable; /* Reset timeout */
sendreq:
- net_core_disconnect(socket);
timeout = *timeout_ptr++;
if (!timeout)
return; /* No file available... */
oldtime = jiffies();
- net_core_connect(socket, url->ip, url->port);
- net_core_send(socket, rrq_packet_buf, rrq_len);
+ /* net_core_open() calls netconn_bind() */
+ net_core_sendto(socket, rrq_packet_buf, rrq_len, url->ip, url->port);
/* If the WRITE call fails, we let the timeout take care of it... */
wait_pkt:
- net_core_disconnect(socket);
for (;;) {
buf_len = sizeof(reply_packet_buf);
@@ -277,8 +275,10 @@ wait_pkt:
if (now - oldtime >= timeout)
goto sendreq;
} else {
- /* Make sure the packet actually came from the server */
- if (src_ip == url->ip)
+ /* Make sure the packet actually came from the server and
+ is long enough for a TFTP opcode */
+ dprintf("tftp_open: got packet buflen=%d\n", buf_len);
+ if ((src_ip == url->ip) && (buf_len >= 2))
break;
}
}
@@ -290,8 +290,6 @@ wait_pkt:
inode->size = -1;
socket->tftp_blksize = TFTP_BLOCKSIZE;
buffersize = buf_len - 2; /* bytes after opcode */
- if (buffersize < 0)
- goto wait_pkt; /* Garbled reply */
/*
* Get the opcode type, and parse it
diff --git a/core/include/net.h b/core/include/net.h
index 4f6819f9..f970ab0f 100644
--- a/core/include/net.h
+++ b/core/include/net.h
@@ -27,6 +27,9 @@ int net_core_recv(struct pxe_pvt_inode *socket, void *buf, uint16_t *buf_len,
void net_core_send(struct pxe_pvt_inode *socket,
const void *data, size_t len);
+void net_core_sendto(struct pxe_pvt_inode *socket, const void *data, size_t len,
+ uint32_t ip, uint16_t port);
+
void probe_undi(void);
void pxe_init_isr(void);
diff --git a/core/legacynet/core.c b/core/legacynet/core.c
index 848410c6..94da6496 100644
--- a/core/legacynet/core.c
+++ b/core/legacynet/core.c
@@ -153,6 +153,43 @@ void net_core_send(struct pxe_pvt_inode *socket, const void *data, size_t len)
}
/**
+ * Send a UDP packet to a destination
+ *
+ * @param:socket, the open socket
+ * @param:data, data buffer to send
+ * @param:len, size of data bufer
+ * @param:ip, the ip address
+ * @param:port, the port number, host-byte order
+ */
+void net_core_sendto(struct pxe_pvt_inode *socket, const void *data, size_t len,
+ uint32_t ip, uint16_t port)
+{
+ static __lowmem struct s_PXENV_UDP_WRITE udp_write;
+ struct net_private_tftp *priv = &socket->net.tftp;
+ void *lbuf;
+ uint16_t tid;
+
+ lbuf = lmalloc(len);
+ if (!lbuf)
+ return;
+
+ memcpy(lbuf, data, len);
+
+ tid = priv->localport; /* TID(local port No) */
+ udp_write.buffer = FAR_PTR(lbuf);
+ udp_write.ip = ip;
+ udp_write.gw = gateway(udp_write.ip);
+ udp_write.src_port = tid;
+ udp_write.dst_port = htons(port);
+ udp_write.buffer_size = len;
+
+ pxe_call(PXENV_UDP_WRITE, &udp_write);
+
+ lfree(lbuf);
+}
+
+
+/**
* Network stack-specific initialization
*
* Initialize UDP stack