[syslinux:master] libupload: Reworking tftp support to use core functions

syslinux-bot for Erwan Velu erwanaliasr1 at gmail.com
Fri Sep 4 09:15:08 PDT 2015


Commit-ID:  62f63cb7706974eba2c34dbea425bf2e9ede0f9e
Gitweb:     http://www.syslinux.org/commit/62f63cb7706974eba2c34dbea425bf2e9ede0f9e
Author:     Erwan Velu <erwanaliasr1 at gmail.com>
AuthorDate: Wed, 2 Sep 2015 20:52:30 +0200
Committer:  Erwan Velu <erwanaliasr1 at gmail.com>
CommitDate: Fri, 4 Sep 2015 17:57:52 +0200

libupload: Reworking tftp support to use core functions

The libupload was using the pxe_call() directly for doing the tftp
uploading stuff. This was only working with pxelinux.

Since we do have lpxelinux, the libupload should use the core functions
to get rid of thoses direct PXE calls.

This patch does
 - add a tftp_put() function which supports core functions.
 - implement the call from libupload making the code much more simplier

As a result {l}pxelinux can upload data to a tftp server is a similar
way. HDT is getting the benefit of such code.

---
 com32/libupload/upload_tftp.c | 179 ++++++++----------------------------------
 core/fs/pxe/tftp.c            | 170 +++++++++++++++++++++++++++++++++++++++
 core/fs/pxe/tftp.h            |  11 +++
 core/fs/pxe/urlparse.c        |   2 +-
 mk/com32.mk                   |   8 +-
 5 files changed, 220 insertions(+), 150 deletions(-)

diff --git a/com32/libupload/upload_tftp.c b/com32/libupload/upload_tftp.c
index 6a0dacb..387113b 100644
--- a/com32/libupload/upload_tftp.c
+++ b/com32/libupload/upload_tftp.c
@@ -8,33 +8,12 @@
 #include <syslinux/config.h>
 #include <netinet/in.h>
 #include <sys/times.h>
+#include <fs/pxe/pxe.h>
+#include <fs/pxe/url.h>
 #include "upload_backend.h"
 
-enum tftp_opcode {
-    TFTP_RRQ	= 1,
-    TFTP_WRQ	= 2,
-    TFTP_DATA	= 3,
-    TFTP_ACK	= 4,
-    TFTP_ERROR	= 5,
-};
-
-struct tftp_error {
-	uint16_t opcode;
-	uint16_t errcode;
-	char errmsg[0];
-} __attribute__ (( packed ));
-
-struct tftp_state {
-    uint32_t my_ip;
-    uint32_t srv_ip;
-    uint32_t srv_gw;
-    uint16_t my_port;
-    uint16_t srv_port;
-    uint16_t seq;
-};
-
 const char *tftp_string_error_message[]={
-"",
+"Unknown error",
 "File not found",
 "Access Denied",
 "Disk Full",
@@ -48,140 +27,46 @@ const char *tftp_string_error_message[]={
 "No Error",
 };
 
-#define RCV_BUF	2048
-
-static int send_ack_packet(struct tftp_state *tftp,
-			   const void *pkt, size_t len)
-{
-    t_PXENV_UDP_WRITE *uw;
-    t_PXENV_UDP_READ  *ur;
-    clock_t start;
-    static const clock_t timeouts[] = {
-	2, 2, 3, 3, 4, 5, 6, 7, 9, 10, 12, 15, 18, 21, 26, 31,
-	37, 44, 53, 64, 77, 92, 110, 132, 159, 191, 229, 0
-    };
-    const clock_t *timeout;
-    int err = -1;
-
-    uw = lmalloc(sizeof *uw + len);
-    ur = lmalloc(sizeof *ur + RCV_BUF);
-
-    for (timeout = timeouts ; *timeout ; timeout++) {
-	memset(uw, 0, sizeof *uw);
-	memcpy(uw+1, pkt, len);
-	uw->ip = tftp->srv_ip;
-	uw->gw = tftp->srv_gw;
-	uw->src_port = tftp->my_port;
-	uw->dst_port = tftp->srv_port ? tftp->srv_port : htons(69);
-	uw->buffer_size = len;
-	uw->buffer = FAR_PTR(uw+1);
-
-	pxe_call(PXENV_UDP_WRITE, uw);
-
-	start = times(NULL);
-
-	do {
-	    memset(ur, 0, sizeof *ur);
-	    ur->src_ip = tftp->srv_ip;
-	    ur->dest_ip = tftp->my_ip;
-	    ur->s_port = tftp->srv_port;
-	    ur->d_port = tftp->my_port;
-	    ur->buffer_size = RCV_BUF;
-	    ur->buffer = FAR_PTR(ur+1);
-
-	    err = pxe_call(PXENV_UDP_READ, ur);
-
-	    if (!err &&	ur->status == PXENV_STATUS_SUCCESS &&
-		tftp->srv_ip == ur->src_ip &&
-		(tftp->srv_port == 0 ||
-		 tftp->srv_port == ur->s_port)) {
-		uint16_t *xb = (uint16_t *)(ur+1);
-		if (ntohs(xb[0]) == TFTP_ACK &&
-		    ntohs(xb[1]) == tftp->seq) {
-		    tftp->srv_port = ur->s_port;
-		    err = TFTP_OK;		/* All good! */
-		    goto done;
-		} else if (ntohs(xb[0]) == TFTP_ERROR) {
-		    struct tftp_error *te = (struct tftp_error *)(ur+1);
-		    if (te->errcode == TFTP_ERR_UNKNOWN_ERROR) {
-		    	tftp_string_error_message[TFTP_ERR_UNKNOWN_ERROR]=strdup(te->errmsg);
-		    }
- 		    err=-ntohs(te->errcode); // Return the associated error code
-		    goto done;
-		}
-	    }
-	} while ((clock_t)(times(NULL) - start) < *timeout);
-    }
-
-done:
-    lfree(ur);
-    lfree(uw);
-
-    return err;
-}
-
-static int upload_tftp_write(struct upload_backend *be)
-{
-    static uint16_t local_port = 0x4000;
-    struct tftp_state tftp;
-    char buffer[512+4+6];
-    int nlen;
-    int err=TFTP_OK;
+static int upload_tftp_write(struct upload_backend *be) {
     const union syslinux_derivative_info *sdi =
 	syslinux_derivative_info();
-    const char *data = be->outbuf;
-    size_t len = be->zbytes;
-    size_t chunk;
-
-    tftp.my_ip    = sdi->pxe.myip;
-    tftp.my_port  = htons(local_port++);
-    tftp.srv_gw   = ((tftp.srv_ip ^ tftp.my_ip) & sdi->pxe.ipinfo->netmask)
-	? sdi->pxe.ipinfo->gateway : 0;
-    tftp.srv_port = 0;
-    tftp.seq      = 0;
+    struct url_info url;
+    struct inode inode;
+    char url_path[255] = {0};
+    uint32_t ip;
+    int err;
 
     if (be->argv[1]) {
-	tftp.srv_ip   = pxe_dns(be->argv[1]);
-	if (!tftp.srv_ip) {
-//	    printf("\nUnable to resolve hostname: %s\n", be->argv[1]);
-	    return -TFTP_ERR_UNABLE_TO_RESOLVE;
-	}
+        ip = pxe_dns(be->argv[1]);
+        if (!ip) {
+            dprintf("\nUnable to resolve hostname: %s\n", be->argv[1]);
+            return -TFTP_ERR_UNABLE_TO_RESOLVE;
+        }
     } else {
-	tftp.srv_ip   = sdi->pxe.ipinfo->serverip;
-	if (!tftp.srv_ip) {
-//	    printf("\nNo server IP address\n");
-	    return -TFTP_ERR_UNABLE_TO_CONNECT;
-	}
+        ip   = sdi->pxe.ipinfo->serverip;
+        if (!ip) {
+            dprintf("\nNo server IP address\n");
+            return -TFTP_ERR_UNABLE_TO_CONNECT;
+        }
     }
 
-/*    printf("server %u.%u.%u.%u... ",
-	   ((uint8_t *)&tftp.srv_ip)[0],
-	   ((uint8_t *)&tftp.srv_ip)[1],
-	   ((uint8_t *)&tftp.srv_ip)[2],
-	   ((uint8_t *)&tftp.srv_ip)[3]);*/
-
-    buffer[0] = 0;
-    buffer[1] = TFTP_WRQ;
-    nlen = strlcpy(buffer+2, be->argv[0], 512);
-    memcpy(buffer+3+nlen, "octet", 6);
-
-    if ((err=send_ack_packet(&tftp, buffer, 2+nlen+1+6))!=TFTP_OK)
-	return err;
+    snprintf(url_path, sizeof(url_path), "tftp://%u.%u.%u.%u/%s",
+	((uint8_t *)&ip)[0],
+	((uint8_t *)&ip)[1],
+	((uint8_t *)&ip)[2],
+	((uint8_t *)&ip)[3],
+	be->argv[0]);
 
-    do {
-	chunk = len >= 512 ? 512 : len;
+    parse_url(&url, url_path);
+    url.ip = ip;
 
-	buffer[1] = TFTP_DATA;
-	*((uint16_t *)(buffer+2)) = htons(++tftp.seq);
-	memcpy(buffer+4, data, chunk);
-	data += chunk;
-	len -= chunk;
+    dprintf("Connecting to %s to send %s\n", url.host, url.path);
+    err = tftp_put(&url, 0, &inode, NULL, be->outbuf, be->zbytes);
 
-	if ((err=send_ack_packet(&tftp, buffer, chunk+4))!=TFTP_OK)
-	    return err;
-    } while (chunk == 512);
+    if (-err != TFTP_OK)
+	printf("upload_tftp_write: TFTP server returned error %d : %s\n", err, tftp_string_error_message[-err]);
 
-    return TFTP_OK;
+    return -err;
 }
 
 struct upload_backend upload_tftp = {
diff --git a/core/fs/pxe/tftp.c b/core/fs/pxe/tftp.c
index 113ede2..594152b 100644
--- a/core/fs/pxe/tftp.c
+++ b/core/fs/pxe/tftp.c
@@ -423,3 +423,173 @@ done:
 
     return;
 }
+
+
+/**
+ * Send a file to a TFTP  server
+ *
+ * @param:inode, the inode to store our state in
+ * @param:ip, the ip to contact to get the file
+ * @param:filename, the file we wanna push
+ *
+ * @out: open_file_t structure, stores in file->open_file
+ * @out: the lenght of this file, stores in file->file_len
+ *
+ */
+__export int tftp_put(struct url_info *url, int flags, struct inode *inode,
+	       const char **redir, char *data, int data_length)
+{
+    struct pxe_pvt_inode *socket = PVT(inode);
+    char *buf;
+    uint16_t buf_len;
+    static const char wrq_tail[] = "octet";
+    char wrq_packet_buf[512+4+6];
+    char reply_packet_buf[PKTBUF_SIZE];
+    int err;
+    int wrq_len;
+    const uint8_t  *timeout_ptr;
+    jiffies_t timeout;
+    jiffies_t oldtime;
+    uint16_t opcode;
+    uint16_t src_port = url->port;
+    uint32_t src_ip;
+    uint16_t seq = 0;
+    size_t chunk = 0;
+    int len = data_length;
+    int return_code = -ntohs(TFTP_EUNDEF);
+
+    (void)redir;		/* TFTP does not redirect */
+    (void)flags;
+
+    if (url->type != URL_OLD_TFTP) {
+	/*
+	 * The TFTP URL specification allows the TFTP to end with a
+	 * ;mode= which we just ignore.
+	 */
+	url_unescape(url->path, ';');
+    }
+
+    if (!src_port)
+	src_port = TFTP_PORT;
+
+//    socket->ops = &tftp_conn_ops;
+    if (core_udp_open(socket))
+	return return_code;
+
+    buf = wrq_packet_buf;
+    *(uint16_t *)buf = TFTP_WRQ;  /* TFTP opcode */
+    buf += 2;
+
+    buf += strlcpy(buf, url->path, 512);
+
+    buf++;			/* Point *past* the final NULL */
+    memcpy(buf, wrq_tail, sizeof wrq_tail);
+    buf += sizeof wrq_tail;
+
+    wrq_len = buf - wrq_packet_buf;
+
+    timeout_ptr = TimeoutTable;   /* Reset timeout */
+sendreq:
+    timeout = *timeout_ptr++;
+    if (!timeout)
+	return return_code;			/* No file available... */
+    oldtime = jiffies();
+
+    core_udp_sendto(socket, wrq_packet_buf, wrq_len, url->ip, src_port);
+
+    /* If the WRITE call fails, we let the timeout take care of it... */
+    for (;;) {
+	buf_len = sizeof(reply_packet_buf);
+
+	err = core_udp_recv(socket, reply_packet_buf, &buf_len,
+			    &src_ip, &src_port);
+	if (err) {
+	    jiffies_t now = jiffies();
+	    if (now - oldtime >= timeout)
+		 goto sendreq;
+	} else {
+	    /* Make sure the packet actually came from the server and
+	       is long enough for a TFTP opcode */
+	    dprintf("tftp_put: got packet buflen=%d from server %u.%u.%u.%u(%u.%u.%u.%u)\n",
+			buf_len,
+			((uint8_t *)&src_ip)[0],
+			((uint8_t *)&src_ip)[1],
+			((uint8_t *)&src_ip)[2],
+			((uint8_t *)&src_ip)[3],
+			((uint8_t *)&url->ip)[0],
+			((uint8_t *)&url->ip)[1],
+			((uint8_t *)&url->ip)[2],
+			((uint8_t *)&url->ip)[3]);
+	    if ((src_ip == url->ip) && (buf_len >= 2))
+		break;
+	}
+    }
+
+    core_udp_disconnect(socket);
+    core_udp_connect(socket, src_ip, src_port);
+
+    /* filesize <- -1 == unknown */
+    inode->size = -1;
+    socket->tftp_blksize = TFTP_BLOCKSIZE;
+
+    /*
+     * Get the opcode type, and parse it
+     */
+    opcode = *(uint16_t *)reply_packet_buf;
+    switch (opcode) {
+    case TFTP_ERROR:
+	dprintf("tftp_push: received a TFTP_ERROR\n");
+	struct tftp_error *te = (struct tftp_error *)(reply_packet_buf+1);
+	return_code = -ntohs(te->errcode);
+        inode->size = 0;
+	goto done;        /* ERROR reply; don't try again */
+
+    case TFTP_ACK:
+	dprintf("tftp_push: received a TFTP_ACK\n");
+	/* We received a ACK packet, sending the associated data packet */
+
+	/* If data was completly sent, we can stop here */
+	if (len == 0) {
+		return_code = -ntohs(TFTP_OK);
+		goto done;
+	}
+
+	/* If the server sequence is not aligned with our, we have an issue
+	 * Let's break the transmission for now but could be improved later */
+	uint16_t srv_seq = ntohs(*(uint16_t *)(reply_packet_buf+2));
+	if (srv_seq != seq) {
+		printf("tftp_push: server sequence (%"PRIu16") is not aligned with our sequence (%"PRIu16"\n", srv_seq, seq);
+		return_code = -ntohs(TFTP_EBADOP);
+		goto done;
+	}
+
+	/* Let's transmit the data block */
+	chunk = len >= 512 ? 512 : len;
+	buf = wrq_packet_buf;
+	*(uint16_t *)buf = TFTP_DATA;  /* TFTP opcode */
+	*((uint16_t *)(buf+2)) = htons(++seq);
+	memcpy(buf+4, data, chunk);
+	wrq_len = chunk + 4;
+	data += chunk;
+	len -= chunk;
+	timeout_ptr = TimeoutTable;   /* Reset timeout */
+	goto sendreq;
+
+    default:
+	dprintf("tftp_push: unknown opcode %d\n", ntohs(opcode));
+	return_code = -ntohs(TFTP_EOPTNEG);
+	goto err_reply;
+    }
+
+err_reply:
+    /* Build the TFTP error packet */
+    dprintf("tftp_push: Failure\n");
+    tftp_error(inode, TFTP_EOPTNEG, "TFTP protocol error");
+    inode->size = 0;
+
+done:
+    if (!inode->size)
+	core_udp_close(socket);
+
+    return return_code;
+}
diff --git a/core/fs/pxe/tftp.h b/core/fs/pxe/tftp.h
index 114c221..8802914 100644
--- a/core/fs/pxe/tftp.h
+++ b/core/fs/pxe/tftp.h
@@ -50,5 +50,16 @@
 #define TFTP_EEXISTS	 htons(6)		// File exists
 #define TFTP_ENOUSER	 htons(7)		// No such user
 #define TFTP_EOPTNEG	 htons(8)		// Option negotiation failure
+#define TFTP_ERESOLVE	 htons(9)		// Not in RFC, internal usage
+#define TFTP_ECONNECT	 htons(10)		// Not in RFC, internal usage
+#define TFTP_OK	 	 htons(11)		// Not in RFC, internal usage
 
+struct tftp_error {
+        uint16_t opcode;
+        uint16_t errcode;
+        char errmsg[0];
+} __attribute__ (( packed ));
+
+int tftp_put(struct url_info *url, int flags, struct inode *inode,
+		               const char **redir, char *data, int data_length);
 #endif /* PXE_TFTP_H */
diff --git a/core/fs/pxe/urlparse.c b/core/fs/pxe/urlparse.c
index 6b73ddb..8b604dc 100644
--- a/core/fs/pxe/urlparse.c
+++ b/core/fs/pxe/urlparse.c
@@ -59,7 +59,7 @@ enum url_type url_type(const char *url)
  * this routine does not allocate any additional storage.  Freeing the
  * original buffer frees all storage used.
  */
-void parse_url(struct url_info *ui, char *url)
+__export void parse_url(struct url_info *ui, char *url)
 {
     char *p = url;
     char *q, *r, *s;
diff --git a/mk/com32.mk b/mk/com32.mk
index 9a3b19d..90eb7ed 100644
--- a/mk/com32.mk
+++ b/mk/com32.mk
@@ -67,12 +67,16 @@ CFLAGS     = $(GCCOPT) $(GCCWARN) \
 	     -fomit-frame-pointer -D__COM32__ -D__FIRMWARE_$(FIRMWARE)__ \
 	     -nostdinc -iwithprefix include \
 	     -I$(com32)/libutil/include -I$(com32)/include \
-	     -I$(com32)/include/sys $(GPLINCLUDE)
+	     -I$(com32)/include/sys $(GPLINCLUDE) \
+	     -I$(topdir)/core \
+	     -I$(topdir)/core/include
 SFLAGS     = $(GCCOPT) $(GCCWARN) \
 	     -fomit-frame-pointer -D__COM32__ -D__FIRMWARE_$(FIRMWARE)__ \
 	     -nostdinc -iwithprefix include \
 	     -I$(com32)/libutil/include -I$(com32)/include \
-	     -I$(com32)/include/sys $(GPLINCLUDE)
+	     -I$(com32)/include/sys $(GPLINCLUDE) \
+	     -I$(topdir)/core \
+	     -I$(topdir)/core/include
 
 COM32LD	   = $(com32)/lib/$(ARCH)/elf.ld
 LDFLAGS    = -m elf_$(ARCH) -shared --hash-style=gnu -T $(COM32LD)


More information about the Syslinux-commits mailing list