summaryrefslogtreecommitdiff
path: root/net
diff options
context:
space:
mode:
Diffstat (limited to 'net')
-rw-r--r--net/lwip/dhcp.c2
-rw-r--r--net/lwip/net-lwip.c45
-rw-r--r--net/lwip/tftp.c188
-rw-r--r--net/net.c4
-rw-r--r--net/net_rand.h2
-rw-r--r--net/nfs-common.c12
6 files changed, 230 insertions, 23 deletions
diff --git a/net/lwip/dhcp.c b/net/lwip/dhcp.c
index 18dc36ae7ca..a5e2e7d4da0 100644
--- a/net/lwip/dhcp.c
+++ b/net/lwip/dhcp.c
@@ -25,7 +25,7 @@ static char boot_file_name[DHCP_BOOT_FILE_LEN];
static void call_lwip_dhcp_fine_tmr(void *ctx)
{
dhcp_fine_tmr();
- sys_timeout(10, call_lwip_dhcp_fine_tmr, NULL);
+ sys_timeout(DHCP_FINE_TIMER_MSECS, call_lwip_dhcp_fine_tmr, NULL);
}
static int dhcp_loop(struct udevice *udev)
diff --git a/net/lwip/net-lwip.c b/net/lwip/net-lwip.c
index cfe5a6a640d..8f8f9d69020 100644
--- a/net/lwip/net-lwip.c
+++ b/net/lwip/net-lwip.c
@@ -40,28 +40,47 @@ char *pxelinux_configfile;
static err_t net_lwip_tx(struct netif *netif, struct pbuf *p)
{
struct udevice *udev = netif->state;
- void *pp = NULL;
+ bool pp_allocated = false;
+ u32 plen;
+ void *pp;
int err;
- if (CONFIG_IS_ENABLED(LWIP_DEBUG_RXTX)) {
- printf("net_lwip_tx: %u bytes, udev %s\n", p->len, udev->name);
- print_hex_dump("net_lwip_tx: ", 0, 16, 1, p->payload, p->len,
- true);
- }
-
- if ((unsigned long)p->payload % PKTALIGN) {
+ if ((unsigned long)p->payload % PKTALIGN || p->len != p->tot_len) {
/*
* Some net drivers have strict alignment requirements and may
* fail or output invalid data if the packet is not aligned.
+ *
+ * A packet may also be stored in multiple chained pbufs. In
+ * this case, assemble the fragments into one contiguous packet
+ * buffer before passing it to the Ethernet driver.
*/
- pp = memalign(PKTALIGN, p->len);
+
+ pp = memalign(PKTALIGN, p->tot_len);
if (!pp)
- return ERR_ABRT;
- memcpy(pp, p->payload, p->len);
+ return ERR_MEM;
+
+ pp_allocated = true;
+
+ plen = pbuf_copy_partial(p, pp, p->tot_len, 0);
+ if (plen != p->tot_len) {
+ free(pp);
+ return ERR_BUF;
+ }
+ } else {
+ pp = p->payload;
+ plen = p->len;
+ }
+
+ if (CONFIG_IS_ENABLED(LWIP_DEBUG_RXTX)) {
+ printf("net_lwip_tx: %u bytes, udev %s\n", plen, udev->name);
+ print_hex_dump("net_lwip_tx: ", 0, 16, 1, pp, plen, true);
}
- err = eth_get_ops(udev)->send(udev, pp ? pp : p->payload, p->len);
- free(pp);
+ err = eth_get_ops(udev)->send(udev, pp, plen);
+
+ if (pp_allocated)
+ free(pp);
+
if (err) {
debug("send error %d\n", err);
return ERR_ABRT;
diff --git a/net/lwip/tftp.c b/net/lwip/tftp.c
index 571c38172f9..d6a9f29a260 100644
--- a/net/lwip/tftp.c
+++ b/net/lwip/tftp.c
@@ -11,6 +11,7 @@
#include <linux/delay.h>
#include <linux/kconfig.h>
#include <lwip/apps/tftp_client.h>
+#include <lwip/apps/tftp_server.h>
#include <lwip/timeouts.h>
#include <mapmem.h>
#include <net.h>
@@ -19,6 +20,8 @@
#define PROGRESS_PRINT_STEP_BYTES (10 * 1024)
/* Max time to wait for first data packet from server */
#define NO_RSP_TIMEOUT_MS 10000
+/* Max time to wait for an incoming TFTP write request */
+#define TFTPSRV_LISTEN_TIMEOUT_MS 50000
enum done_state {
NOT_DONE = 0,
@@ -34,8 +37,31 @@ struct tftp_ctx {
ulong hash_count;
ulong start_time;
enum done_state done;
+ bool is_server;
+ bool wrq_accepted;
+ char fname[TFTP_MAX_FILENAME_LEN + 1];
};
+/*
+ * The lwIP TFTP server open callback has no user-data argument. Keep the
+ * current server context here so tftp_open() can return it.
+ */
+static struct tftp_ctx *tftpsrv_active_ctx;
+
+static void transfer_timeout(void *arg)
+{
+ struct tftp_ctx *ctx = (struct tftp_ctx *)arg;
+
+ printf("Timeout!\n");
+ ctx->done = FAILURE;
+}
+
+static void restart_transfer_timeout(struct tftp_ctx *ctx)
+{
+ sys_untimeout(transfer_timeout, ctx);
+ sys_timeout(TFTP_TIMEOUT_MSECS, transfer_timeout, ctx);
+}
+
/**
* store_block() - copy received data
*
@@ -71,7 +97,7 @@ static int store_block(struct tftp_ctx *ctx, void *src, u16_t len)
ctx->size += len;
ctx->block_count++;
- tftp_tsize = tftp_client_get_tsize();
+ tftp_tsize = ctx->is_server ? 0 : tftp_client_get_tsize();
if (tftp_tsize) {
pos = clamp(ctx->size, 0UL, tftp_tsize);
@@ -92,7 +118,20 @@ static int store_block(struct tftp_ctx *ctx, void *src, u16_t len)
static void *tftp_open(const char *fname, const char *mode, u8_t is_write)
{
- return NULL;
+ struct tftp_ctx *ctx = tftpsrv_active_ctx;
+
+ if (!IS_ENABLED(CONFIG_CMD_TFTPSRV) || !ctx || !is_write)
+ return NULL;
+
+ ctx->wrq_accepted = true;
+ ctx->start_time = get_timer(0);
+ snprintf(ctx->fname, sizeof(ctx->fname), "%s", fname);
+ restart_transfer_timeout(ctx);
+
+ printf("\nReceiving '%s' mode '%s'\n", fname, mode);
+ puts("Loading: ");
+
+ return ctx;
}
static void tftp_close(void *handle)
@@ -101,13 +140,15 @@ static void tftp_close(void *handle)
ulong tftp_tsize;
ulong elapsed;
+ sys_untimeout(transfer_timeout, ctx);
+
if (ctx->done == FAILURE || ctx->done == ABORTED) {
/* Closing after an error or Ctrl-C */
return;
}
ctx->done = SUCCESS;
- tftp_tsize = tftp_client_get_tsize();
+ tftp_tsize = ctx->is_server ? 0 : tftp_client_get_tsize();
if (tftp_tsize) {
/* Print hash marks for the last packet received */
while (ctx->hash_count < 49) {
@@ -142,9 +183,14 @@ static int tftp_write(void *handle, struct pbuf *p)
struct tftp_ctx *ctx = handle;
struct pbuf *q;
- for (q = p; q; q = q->next)
- if (store_block(ctx, q->payload, q->len) < 0)
+ for (q = p; q; q = q->next) {
+ if (store_block(ctx, q->payload, q->len) < 0) {
+ ctx->done = FAILURE;
return -1;
+ }
+ }
+
+ restart_transfer_timeout(ctx);
return 0;
}
@@ -204,6 +250,9 @@ static int tftp_loop(struct udevice *udev, ulong addr, char *fname,
ctx.block_count = 0;
ctx.hash_count = 0;
ctx.daddr = addr;
+ ctx.is_server = false;
+ ctx.wrq_accepted = false;
+ ctx.fname[0] = '\0';
printf("Using %s device\n", udev->name);
printf("TFTP from server %s; our IP address is %s\n",
@@ -258,6 +307,135 @@ static int tftp_loop(struct udevice *udev, ulong addr, char *fname,
return -1;
}
+static void no_request(void *arg)
+{
+ struct tftp_ctx *ctx = (struct tftp_ctx *)arg;
+
+ if (ctx->wrq_accepted)
+ return;
+
+ printf("Timeout!\n");
+ ctx->done = FAILURE;
+}
+
+static int tftpsrv_loop(struct udevice *udev, ulong addr)
+{
+ struct netif *netif;
+ struct tftp_ctx ctx;
+ const char *ipaddr;
+ int ret = -1;
+ err_t err;
+
+ if (addr == 0)
+ return -1;
+
+ ipaddr = env_get("ipaddr");
+ if (!ipaddr || !*ipaddr) {
+ log_err("error: ipaddr has to be set\n");
+ return -1;
+ }
+
+ netif = net_lwip_new_netif(udev);
+ if (!netif)
+ return -1;
+
+ memset(&ctx, 0, sizeof(ctx));
+ ctx.done = NOT_DONE;
+ ctx.daddr = addr;
+ ctx.is_server = true;
+
+ printf("Using %s device\n", udev->name);
+ printf("Listening for TFTP transfer on %s\n", ipaddr);
+ printf("Load address: 0x%lx\n", ctx.daddr);
+
+ tftpsrv_active_ctx = &ctx;
+ err = tftp_init_server(&tftp_context);
+ if (err != ERR_OK) {
+ log_err("tftp_init_server err: %d\n", err);
+ goto out_remove_netif;
+ }
+
+ ctx.start_time = get_timer(0);
+ sys_timeout(TFTPSRV_LISTEN_TIMEOUT_MS, no_request, &ctx);
+ while (!ctx.done) {
+ net_lwip_rx(udev, netif);
+ if (ctrlc()) {
+ printf("\nAbort\n");
+ ctx.done = ABORTED;
+ break;
+ }
+ }
+ sys_untimeout(no_request, &ctx);
+ sys_untimeout(transfer_timeout, &ctx);
+
+ tftp_cleanup();
+
+ if (ctx.done == SUCCESS) {
+ if (env_set_hex("fileaddr", addr)) {
+ log_err("fileaddr not updated\n");
+ goto out_remove_netif;
+ }
+ efi_set_bootdev("Net", "", ctx.fname, map_sysmem(addr, 0),
+ ctx.size);
+ ret = 0;
+ }
+
+out_remove_netif:
+ tftpsrv_active_ctx = NULL;
+ net_lwip_remove_netif(netif);
+
+ return ret;
+}
+
+int do_tftpsrv(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[])
+{
+ int ret = CMD_RET_SUCCESS;
+ char *end;
+ ulong laddr;
+ ulong addr;
+
+ if (!IS_ENABLED(CONFIG_CMD_TFTPSRV))
+ return CMD_RET_FAILURE;
+
+ laddr = env_get_ulong("loadaddr", 16, image_load_addr);
+
+ switch (argc) {
+ case 1:
+ break;
+ case 2:
+ addr = hextoul(argv[1], &end);
+ if (end == argv[1] || *end) {
+ ret = CMD_RET_USAGE;
+ goto out;
+ }
+ laddr = addr;
+ break;
+ default:
+ ret = CMD_RET_USAGE;
+ goto out;
+ }
+
+ if (!laddr) {
+ log_err("error: no load address\n");
+ ret = CMD_RET_FAILURE;
+ goto out;
+ }
+
+ if (net_lwip_eth_start() < 0) {
+ ret = CMD_RET_FAILURE;
+ goto out;
+ }
+
+ if (tftpsrv_loop(eth_get_dev(), laddr) < 0)
+ ret = CMD_RET_FAILURE;
+ else
+ image_load_addr = laddr;
+ net_lwip_eth_stop();
+
+out:
+ return ret;
+}
+
int do_tftpb(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[])
{
int ret = CMD_RET_SUCCESS;
diff --git a/net/net.c b/net/net.c
index 61c5a6ef6c4..71666eb1113 100644
--- a/net/net.c
+++ b/net/net.c
@@ -1076,6 +1076,8 @@ static struct ip_udp_hdr *__net_defragment(struct ip_udp_hdr *ip, int *lenp)
} else if (h >= thisfrag) {
/* overlaps with initial part of the hole: move this hole */
newh = thisfrag + (len / 8);
+ if ((uchar *)(newh + 1) > pkt_buff + IP_PKTSIZE)
+ return NULL; /* hole descriptor would overflow pkt_buff */
*newh = *h;
h = newh;
if (h->next_hole)
@@ -1088,6 +1090,8 @@ static struct ip_udp_hdr *__net_defragment(struct ip_udp_hdr *ip, int *lenp)
} else {
/* fragment sits in the middle: split the hole */
newh = thisfrag + (len / 8);
+ if ((uchar *)(newh + 1) > pkt_buff + IP_PKTSIZE)
+ return NULL; /* hole descriptor would overflow pkt_buff */
*newh = *h;
h->last_byte = start;
h->next_hole = (newh - payload);
diff --git a/net/net_rand.h b/net/net_rand.h
index e21dff8569b..18ff7b68056 100644
--- a/net/net_rand.h
+++ b/net/net_rand.h
@@ -44,7 +44,7 @@ static inline void srand_mac(void)
if (CONFIG_IS_ENABLED(DM_RNG)) {
ret = uclass_get_device(UCLASS_RNG, 0, &devp);
- if (ret) {
+ if (!ret) {
ret = dm_rng_read(devp, &randv, sizeof(randv));
if (ret < 0)
randv = 0;
diff --git a/net/nfs-common.c b/net/nfs-common.c
index 72d8fd823e3..637fcfd9bb8 100644
--- a/net/nfs-common.c
+++ b/net/nfs-common.c
@@ -671,18 +671,24 @@ static int nfs_readlink_reply(uchar *pkt, unsigned int len)
if (*((char *)&rpc_pkt.u.reply.data[2 + nfsv3_data_offset]) != '/') {
int pathlen;
+ int new_len;
strcat(nfs_path, "/");
pathlen = strlen(nfs_path);
- if (pathlen + rlen >= sizeof(nfs_path_buff))
+ new_len = pathlen + rlen;
+ if (new_len >= sizeof(nfs_path_buff)) {
+ printf("NFS: symlink too long (%d bytes)\n", new_len);
return -NFS_RPC_DROP;
+ }
memcpy(nfs_path + pathlen,
(uchar *)&rpc_pkt.u.reply.data[2 + nfsv3_data_offset],
rlen);
- nfs_path[pathlen + rlen] = 0;
+ nfs_path[new_len] = 0;
} else {
- if (rlen >= sizeof(nfs_path_buff))
+ if (rlen >= sizeof(nfs_path_buff)) {
+ printf("NFS: symlink too long (%d bytes)\n", rlen);
return -NFS_RPC_DROP;
+ }
memcpy(nfs_path,
(uchar *)&rpc_pkt.u.reply.data[2 + nfsv3_data_offset],
rlen);