diff options
Diffstat (limited to 'net')
| -rw-r--r-- | net/lwip/dhcp.c | 2 | ||||
| -rw-r--r-- | net/lwip/net-lwip.c | 45 | ||||
| -rw-r--r-- | net/lwip/tftp.c | 188 | ||||
| -rw-r--r-- | net/net.c | 4 | ||||
| -rw-r--r-- | net/net_rand.h | 2 | ||||
| -rw-r--r-- | net/nfs-common.c | 12 |
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); |
