Message ID | 20250101033326.439585-1-mikhail.kshevetskiy@iopsys.eu |
---|---|
State | New |
Delegated to: | Ramon Fried |
Headers | show |
Series | net/netcat: add netcat over tcp support | expand |
On 1/1/25 04:33, Mikhail Kshevetskiy wrote: > This patch adds downloading/uploading of data with netcat utility. > Client/server modes are supported both. > > This patch is also an example of using new tcp api for legacy stack. > > How to test: > ============ > netcat-openbsd=1.219-1 from debian were used for a tests > > a) Load data from remote host. > * U-Boot listen on tcp port 3456 > * PC connects > > u-boot: netcat load ${loadaddr} 3456 > PC: netcat -q1 ${UBOOT_IP} 3456 < image.itb > > b) Load data from remote host. > * PC listen on tcp port 3456 > * U-Boot connects > > PC: netcat -q1 -l -p 3456 < image.itb > u-boot: netcat load ${loadaddr} ${PC_IP}:3456 > > c) Save data to remote host > * U-Boot listen on tcp port 3456 > * PC connects > > u-boot: netcat save ${loadaddr} ${data_size_in_hex} 3456 > PC: netcat -w1 ${UBOOT_IP} 3456 >image.itb > > d) Save data to remote host > * PC listen on tcp port 3456 > * U-Boot connects > > PC: netcat -w1 -l -p 3456 >image.itb > u-boot: netcat save ${loadaddr} ${data_size_in_hex} ${PC_IP}:3456 > > Signed-off-by: Mikhail Kshevetskiy <mikhail.kshevetskiy@iopsys.eu> > Reviewed-by: Simon Glass <sjg@chromium.org> > --- > cmd/Kconfig | 10 +++ > cmd/net.c | 35 ++++++-- > include/net-legacy.h | 2 +- > include/net/netcat.h | 20 +++++ > net/Makefile | 1 + > net/net.c | 9 ++ > net/netcat.c | 194 +++++++++++++++++++++++++++++++++++++++++++ > 7 files changed, 265 insertions(+), 6 deletions(-) > create mode 100644 include/net/netcat.h > create mode 100644 net/netcat.c > > diff --git a/cmd/Kconfig b/cmd/Kconfig > index 93efeaec6f4..e37e50a18b5 100644 > --- a/cmd/Kconfig > +++ b/cmd/Kconfig > @@ -2091,6 +2091,16 @@ config CMD_DNS > help > Lookup the IP of a hostname > > +config CMD_NETCAT > + bool "netcat" > + select PROT_TCP Thank you for your patch which looks like a useful addition to U-Boot. Here you make the netcat command depend on CMD_NET. This dependency seems unnecessary at least for the lwIP case. > + help > + netcat is a simple command to load/store kernel, or other files, > + using well-known netcat (nc) utility. Unlike classic netcat utility > + this command supports TCP-based data transfer only, thus no data > + will be lost or reordered. Any netcat implementation should work, > + but openbsd one was tested only. > + > config CMD_MII > bool "mii" > imply CMD_MDIO > diff --git a/cmd/net.c b/cmd/net.c > index 79525f73a51..d7a5025f940 100644 > --- a/cmd/net.c > +++ b/cmd/net.c > @@ -208,6 +208,29 @@ U_BOOT_CMD( > ); > #endif > > +#if defined(CONFIG_CMD_NETCAT) > +static int do_netcat_load(struct cmd_tbl *cmdtp, int flag, int argc, > + char *const argv[]) > +{ > + return netboot_common(NETCAT_LOAD, cmdtp, argc, argv); > +} > + > +static int do_netcat_save(struct cmd_tbl *cmdtp, int flag, int argc, > + char *const argv[]) > +{ > + return netboot_common(NETCAT_SAVE, cmdtp, argc, argv); > +} > + > +U_BOOT_LONGHELP(netcat, > + "load [loadAddress] [[hostIPaddr:]port]\n" > + "save Address Size [[hostIPaddr:]port]"); > + > +U_BOOT_CMD_WITH_SUBCMDS(netcat, > + "load/store data over plain TCP via netcat utility", netcat_help_text, > + U_BOOT_SUBCMD_MKENT(load, 3, 0, do_netcat_load), > + U_BOOT_SUBCMD_MKENT(save, 4, 0, do_netcat_save)); > +#endif > + > static void netboot_update_env(void) > { > char tmp[46]; > @@ -325,16 +348,17 @@ static int parse_args(enum proto_t proto, int argc, char *const argv[]) > > switch (argc) { > case 1: > - if (IS_ENABLED(CONFIG_CMD_TFTPPUT) && proto == TFTPPUT) > + if ((IS_ENABLED(CONFIG_CMD_TFTPPUT) && proto == TFTPPUT) || > + (IS_ENABLED(CONFIG_CMD_NETCAT) && proto == NETCAT_SAVE)) > return 1; > - > /* refresh bootfile name from env */ > copy_filename(net_boot_file_name, env_get("bootfile"), > sizeof(net_boot_file_name)); > break; > > case 2: > - if (IS_ENABLED(CONFIG_CMD_TFTPPUT) && proto == TFTPPUT) > + if ((IS_ENABLED(CONFIG_CMD_TFTPPUT) && proto == TFTPPUT) || > + (IS_ENABLED(CONFIG_CMD_NETCAT) && proto == NETCAT_SAVE)) > return 1; > /* > * Only one arg - accept two forms: > @@ -356,7 +380,8 @@ static int parse_args(enum proto_t proto, int argc, char *const argv[]) > break; > > case 3: > - if (IS_ENABLED(CONFIG_CMD_TFTPPUT) && proto == TFTPPUT) { > + if ((IS_ENABLED(CONFIG_CMD_TFTPPUT) && proto == TFTPPUT) || > + (IS_ENABLED(CONFIG_CMD_NETCAT) && proto == NETCAT_SAVE)) { > if (parse_addr_size(argv)) > return 1; > } else { > @@ -367,7 +392,7 @@ static int parse_args(enum proto_t proto, int argc, char *const argv[]) > } > break; > > -#ifdef CONFIG_CMD_TFTPPUT > +#if defined(CONFIG_CMD_TFTPPUT) || defined(CONFIG_CMD_NETCAT) > case 4: > if (parse_addr_size(argv)) > return 1; > diff --git a/include/net-legacy.h b/include/net-legacy.h > index bc0f0cde9fe..84a4beb9ec1 100644 > --- a/include/net-legacy.h > +++ b/include/net-legacy.h > @@ -305,7 +305,7 @@ extern int net_restart_wrap; /* Tried all network devices */ > enum proto_t { > BOOTP, RARP, ARP, TFTPGET, DHCP, DHCP6, PING, PING6, DNS, NFS, CDP, > NETCONS, SNTP, TFTPSRV, TFTPPUT, LINKLOCAL, FASTBOOT_UDP, FASTBOOT_TCP, > - WOL, UDP, NCSI, WGET, RS > + WOL, UDP, NCSI, WGET, NETCAT_LOAD, NETCAT_SAVE, RS > }; > > /* Indicates whether the file name was specified on the command line */ > diff --git a/include/net/netcat.h b/include/net/netcat.h > new file mode 100644 > index 00000000000..d8e09aaaa55 > --- /dev/null > +++ b/include/net/netcat.h > @@ -0,0 +1,20 @@ > +/* SPDX-License-Identifier: BSD-2-Clause According to doc/develop/sending_patches.rst all new code should use the GPL-2.0-or-later license. > + * > + * netcat include file > + * Copyright (C) 2024 IOPSYS Software Solutions AB > + * Author: Mikhail Kshevetskiy <mikhail.kshevetskiy@iopsys.eu> > + */ > +#ifndef __NET_NETCAT_TCP_H__ > +#define __NET_NETCAT_TCP_H__ > + > +/** > + * netcat_load_start() - begin netcat in loading mode > + */ > +void netcat_load_start(void); > + > +/** > + * netcat_save_start() - begin netcat in data saving mode > + */ > +void netcat_save_start(void); > + > +#endif /* __NET_NETCAT_TCP_H__ */ > diff --git a/net/Makefile b/net/Makefile > index 7c917b318c0..bc5f2f123be 100644 > --- a/net/Makefile > +++ b/net/Makefile > @@ -30,6 +30,7 @@ obj-$(CONFIG_CMD_WOL) += wol.o > obj-$(CONFIG_PROT_UDP) += udp.o > obj-$(CONFIG_PROT_TCP) += tcp.o > obj-$(CONFIG_WGET) += wget.o > +obj-$(CONFIG_CMD_NETCAT) += netcat.o > > # Disable this warning as it is triggered by: > # sprintf(buf, index ? "foo%d" : "foo", index) > diff --git a/net/net.c b/net/net.c > index 1828f1cca36..595bffdc532 100644 > --- a/net/net.c > +++ b/net/net.c > @@ -103,6 +103,7 @@ > #include <net/fastboot_udp.h> > #include <net/fastboot_tcp.h> > #include <net/ncsi.h> > +#include <net/netcat.h> > #if defined(CONFIG_CMD_PCAP) > #include <net/pcap.h> > #endif > @@ -572,6 +573,14 @@ restart: > wget_start(); > break; > #endif > +#if defined(CONFIG_CMD_NETCAT) > + case NETCAT_LOAD: > + netcat_load_start(); > + break; > + case NETCAT_SAVE: > + netcat_save_start(); > + break; > +#endif > #if defined(CONFIG_CMD_CDP) > case CDP: > cdp_start(); > diff --git a/net/netcat.c b/net/netcat.c > new file mode 100644 > index 00000000000..47dbebe5ae2 > --- /dev/null > +++ b/net/netcat.c > @@ -0,0 +1,194 @@ > +// SPDX-License-Identifier: GPL-2.0 According to doc/develop/sending_patches.rst all new code should use the GPL-2.0-or-later license. > +/* > + * netcat support driver > + * Copyright (C) 2024 IOPSYS Software Solutions AB > + * Author: Mikhail Kshevetskiy <mikhail.kshevetskiy@iopsys.eu> > + */ > + > +#include <command.h> > +#include <display_options.h> > +#include <env.h> > +#include <image.h> > +#include <mapmem.h> > +#include <net.h> > +#include <net/tcp.h> > +#include <net/netcat.h> > + > +#define HASHES_PER_LINE 65 > +#define MARKER_STEP 10 > + > +static struct in_addr server_ip; > +static u16 server_port; > +static u16 local_port; > +static int listen; > +static int reading; > +static int marker, marks_in_line; > +static enum net_loop_state netcat_loop_state; > + Please, describe all functions in Sphinx style. Cf. https://docs.kernel.org/doc-guide/kernel-doc.html#function-documentation > +static void show_block_marker(int packets) > +{ > + for (; marker + MARKER_STEP <= packets; marker += MARKER_STEP) { > + marks_in_line++; > + putc('#'); > + if (marks_in_line == HASHES_PER_LINE) { > + marks_in_line = 0; > + puts("\n"); > + } > + } > +} > + > +static void tcp_stream_on_closed(struct tcp_stream *tcp) > +{ > + if (tcp->status != TCP_ERR_OK) > + netcat_loop_state = NETLOOP_FAIL; > + > + /* > + * The status line will be shown after the download/upload > + * progress (see show_block_marker() function). This progress > + * generally have no final "\n", so jump to new line before > + * output the status > + */ > + if (netcat_loop_state != NETLOOP_SUCCESS) { > + net_boot_file_size = 0; > + printf("\nnetcat: Transfer Fail, TCP status - %d\n", tcp->status); > + } else { > + env_set_hex("filesize", net_boot_file_size); > + printf("\nPackets %s %d, Transfer Successful\n", > + reading ? "received" : "transmitted", > + reading ? tcp->rx_packets : tcp->tx_packets); > + } > + net_set_state(netcat_loop_state); > +} > + > +static void tcp_stream_on_rcv_nxt_update(struct tcp_stream *tcp, u32 rx_bytes) > +{ > + net_boot_file_size = rx_bytes; > + show_block_marker(tcp->rx_packets); > +} > + > +static void tcp_stream_on_snd_una_update(struct tcp_stream *tcp, u32 tx_bytes) > +{ > + show_block_marker(tcp->tx_packets); > + if (tx_bytes == image_save_size) > + tcp_stream_close(tcp); > +} > + > +static void tcp_stream_on_established(struct tcp_stream *tcp) > +{ > + netcat_loop_state = NETLOOP_SUCCESS; > +} > + > +static int tcp_stream_rx(struct tcp_stream *tcp, u32 rx_offs, void *buf, int len) > +{ > + void *ptr; > + > + ptr = map_sysmem(image_load_addr + rx_offs, len); > + memcpy(ptr, buf, len); > + unmap_sysmem(ptr); > + > + return len; > +} > + > +static int tcp_stream_tx(struct tcp_stream *tcp, u32 tx_offs, void *buf, int maxlen) > +{ > + void *ptr; > + > + if (tx_offs + maxlen > image_save_size) > + maxlen = image_save_size - tx_offs; > + if (!maxlen) > + return 0; > + > + ptr = map_sysmem(image_save_addr + tx_offs, maxlen); > + memcpy(buf, ptr, maxlen); > + unmap_sysmem(ptr); > + > + return maxlen; > +} > + > +/* > + * This function will be called on new connections (incoming or outgoing) > + * It MUST: > + * -- setup required tcp_stream callbacks (if connection will be accepted) > + * -- return a connection verdict: > + * 0: discard connection > + * 1: accept connection Please, use a Sphinx style function description. Best regards Heinrich > + */ > +static int tcp_stream_on_create(struct tcp_stream *tcp) > +{ > + if (listen) { > + /* > + * We are listening for incoming connections. > + * Accept connections to netcat listen port only. > + */ > + if (tcp->lport != local_port) > + return 0; > + } else { > + /* > + * We are connecting to remote host. > + * Check remote IP:port to make sure that this is our connection > + */ > + if (tcp->rhost.s_addr != server_ip.s_addr || > + tcp->rport != server_port) > + return 0; > + } > + > + netcat_loop_state = NETLOOP_FAIL; > + net_boot_file_size = 0; > + marker = 0; > + marks_in_line = 0; > + > + tcp->on_closed = tcp_stream_on_closed; > + tcp->on_established = tcp_stream_on_established; > + if (reading) { > + tcp->on_rcv_nxt_update = tcp_stream_on_rcv_nxt_update; > + tcp->rx = tcp_stream_rx; > + } else { > + tcp->on_snd_una_update = tcp_stream_on_snd_una_update; > + tcp->tx = tcp_stream_tx; > + } > + > + return 1; > +} > + > +static void netcat_start(void) > +{ > + struct tcp_stream *tcp; > + > + memset(net_server_ethaddr, 0, 6); > + tcp_stream_set_on_create_handler(tcp_stream_on_create); > + > + if (!strchr(net_boot_file_name, ':')) { > + listen = 1; > + printf("Listening on port %s...\n", net_boot_file_name); > + > + local_port = dectoul(net_boot_file_name, NULL); > + } else { > + listen = 0; > + printf("Connecting to %s...\n", net_boot_file_name); > + > + server_ip = string_to_ip(net_boot_file_name); > + server_port = dectoul(strchr(net_boot_file_name, ':') + 1, NULL); > + > + tcp = tcp_stream_connect(server_ip, server_port); > + if (!tcp) { > + printf("No free tcp streams\n"); > + net_set_state(NETLOOP_FAIL); > + return; > + } > + tcp_stream_put(tcp); > + } > +} > + > +void netcat_load_start(void) > +{ > + reading = 1; > + > + return netcat_start(); > +} > + > +void netcat_save_start(void) > +{ > + reading = 0; > + > + return netcat_start(); > +}
diff --git a/cmd/Kconfig b/cmd/Kconfig index 93efeaec6f4..e37e50a18b5 100644 --- a/cmd/Kconfig +++ b/cmd/Kconfig @@ -2091,6 +2091,16 @@ config CMD_DNS help Lookup the IP of a hostname +config CMD_NETCAT + bool "netcat" + select PROT_TCP + help + netcat is a simple command to load/store kernel, or other files, + using well-known netcat (nc) utility. Unlike classic netcat utility + this command supports TCP-based data transfer only, thus no data + will be lost or reordered. Any netcat implementation should work, + but openbsd one was tested only. + config CMD_MII bool "mii" imply CMD_MDIO diff --git a/cmd/net.c b/cmd/net.c index 79525f73a51..d7a5025f940 100644 --- a/cmd/net.c +++ b/cmd/net.c @@ -208,6 +208,29 @@ U_BOOT_CMD( ); #endif +#if defined(CONFIG_CMD_NETCAT) +static int do_netcat_load(struct cmd_tbl *cmdtp, int flag, int argc, + char *const argv[]) +{ + return netboot_common(NETCAT_LOAD, cmdtp, argc, argv); +} + +static int do_netcat_save(struct cmd_tbl *cmdtp, int flag, int argc, + char *const argv[]) +{ + return netboot_common(NETCAT_SAVE, cmdtp, argc, argv); +} + +U_BOOT_LONGHELP(netcat, + "load [loadAddress] [[hostIPaddr:]port]\n" + "save Address Size [[hostIPaddr:]port]"); + +U_BOOT_CMD_WITH_SUBCMDS(netcat, + "load/store data over plain TCP via netcat utility", netcat_help_text, + U_BOOT_SUBCMD_MKENT(load, 3, 0, do_netcat_load), + U_BOOT_SUBCMD_MKENT(save, 4, 0, do_netcat_save)); +#endif + static void netboot_update_env(void) { char tmp[46]; @@ -325,16 +348,17 @@ static int parse_args(enum proto_t proto, int argc, char *const argv[]) switch (argc) { case 1: - if (IS_ENABLED(CONFIG_CMD_TFTPPUT) && proto == TFTPPUT) + if ((IS_ENABLED(CONFIG_CMD_TFTPPUT) && proto == TFTPPUT) || + (IS_ENABLED(CONFIG_CMD_NETCAT) && proto == NETCAT_SAVE)) return 1; - /* refresh bootfile name from env */ copy_filename(net_boot_file_name, env_get("bootfile"), sizeof(net_boot_file_name)); break; case 2: - if (IS_ENABLED(CONFIG_CMD_TFTPPUT) && proto == TFTPPUT) + if ((IS_ENABLED(CONFIG_CMD_TFTPPUT) && proto == TFTPPUT) || + (IS_ENABLED(CONFIG_CMD_NETCAT) && proto == NETCAT_SAVE)) return 1; /* * Only one arg - accept two forms: @@ -356,7 +380,8 @@ static int parse_args(enum proto_t proto, int argc, char *const argv[]) break; case 3: - if (IS_ENABLED(CONFIG_CMD_TFTPPUT) && proto == TFTPPUT) { + if ((IS_ENABLED(CONFIG_CMD_TFTPPUT) && proto == TFTPPUT) || + (IS_ENABLED(CONFIG_CMD_NETCAT) && proto == NETCAT_SAVE)) { if (parse_addr_size(argv)) return 1; } else { @@ -367,7 +392,7 @@ static int parse_args(enum proto_t proto, int argc, char *const argv[]) } break; -#ifdef CONFIG_CMD_TFTPPUT +#if defined(CONFIG_CMD_TFTPPUT) || defined(CONFIG_CMD_NETCAT) case 4: if (parse_addr_size(argv)) return 1; diff --git a/include/net-legacy.h b/include/net-legacy.h index bc0f0cde9fe..84a4beb9ec1 100644 --- a/include/net-legacy.h +++ b/include/net-legacy.h @@ -305,7 +305,7 @@ extern int net_restart_wrap; /* Tried all network devices */ enum proto_t { BOOTP, RARP, ARP, TFTPGET, DHCP, DHCP6, PING, PING6, DNS, NFS, CDP, NETCONS, SNTP, TFTPSRV, TFTPPUT, LINKLOCAL, FASTBOOT_UDP, FASTBOOT_TCP, - WOL, UDP, NCSI, WGET, RS + WOL, UDP, NCSI, WGET, NETCAT_LOAD, NETCAT_SAVE, RS }; /* Indicates whether the file name was specified on the command line */ diff --git a/include/net/netcat.h b/include/net/netcat.h new file mode 100644 index 00000000000..d8e09aaaa55 --- /dev/null +++ b/include/net/netcat.h @@ -0,0 +1,20 @@ +/* SPDX-License-Identifier: BSD-2-Clause + * + * netcat include file + * Copyright (C) 2024 IOPSYS Software Solutions AB + * Author: Mikhail Kshevetskiy <mikhail.kshevetskiy@iopsys.eu> + */ +#ifndef __NET_NETCAT_TCP_H__ +#define __NET_NETCAT_TCP_H__ + +/** + * netcat_load_start() - begin netcat in loading mode + */ +void netcat_load_start(void); + +/** + * netcat_save_start() - begin netcat in data saving mode + */ +void netcat_save_start(void); + +#endif /* __NET_NETCAT_TCP_H__ */ diff --git a/net/Makefile b/net/Makefile index 7c917b318c0..bc5f2f123be 100644 --- a/net/Makefile +++ b/net/Makefile @@ -30,6 +30,7 @@ obj-$(CONFIG_CMD_WOL) += wol.o obj-$(CONFIG_PROT_UDP) += udp.o obj-$(CONFIG_PROT_TCP) += tcp.o obj-$(CONFIG_WGET) += wget.o +obj-$(CONFIG_CMD_NETCAT) += netcat.o # Disable this warning as it is triggered by: # sprintf(buf, index ? "foo%d" : "foo", index) diff --git a/net/net.c b/net/net.c index 1828f1cca36..595bffdc532 100644 --- a/net/net.c +++ b/net/net.c @@ -103,6 +103,7 @@ #include <net/fastboot_udp.h> #include <net/fastboot_tcp.h> #include <net/ncsi.h> +#include <net/netcat.h> #if defined(CONFIG_CMD_PCAP) #include <net/pcap.h> #endif @@ -572,6 +573,14 @@ restart: wget_start(); break; #endif +#if defined(CONFIG_CMD_NETCAT) + case NETCAT_LOAD: + netcat_load_start(); + break; + case NETCAT_SAVE: + netcat_save_start(); + break; +#endif #if defined(CONFIG_CMD_CDP) case CDP: cdp_start(); diff --git a/net/netcat.c b/net/netcat.c new file mode 100644 index 00000000000..47dbebe5ae2 --- /dev/null +++ b/net/netcat.c @@ -0,0 +1,194 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * netcat support driver + * Copyright (C) 2024 IOPSYS Software Solutions AB + * Author: Mikhail Kshevetskiy <mikhail.kshevetskiy@iopsys.eu> + */ + +#include <command.h> +#include <display_options.h> +#include <env.h> +#include <image.h> +#include <mapmem.h> +#include <net.h> +#include <net/tcp.h> +#include <net/netcat.h> + +#define HASHES_PER_LINE 65 +#define MARKER_STEP 10 + +static struct in_addr server_ip; +static u16 server_port; +static u16 local_port; +static int listen; +static int reading; +static int marker, marks_in_line; +static enum net_loop_state netcat_loop_state; + +static void show_block_marker(int packets) +{ + for (; marker + MARKER_STEP <= packets; marker += MARKER_STEP) { + marks_in_line++; + putc('#'); + if (marks_in_line == HASHES_PER_LINE) { + marks_in_line = 0; + puts("\n"); + } + } +} + +static void tcp_stream_on_closed(struct tcp_stream *tcp) +{ + if (tcp->status != TCP_ERR_OK) + netcat_loop_state = NETLOOP_FAIL; + + /* + * The status line will be shown after the download/upload + * progress (see show_block_marker() function). This progress + * generally have no final "\n", so jump to new line before + * output the status + */ + if (netcat_loop_state != NETLOOP_SUCCESS) { + net_boot_file_size = 0; + printf("\nnetcat: Transfer Fail, TCP status - %d\n", tcp->status); + } else { + env_set_hex("filesize", net_boot_file_size); + printf("\nPackets %s %d, Transfer Successful\n", + reading ? "received" : "transmitted", + reading ? tcp->rx_packets : tcp->tx_packets); + } + net_set_state(netcat_loop_state); +} + +static void tcp_stream_on_rcv_nxt_update(struct tcp_stream *tcp, u32 rx_bytes) +{ + net_boot_file_size = rx_bytes; + show_block_marker(tcp->rx_packets); +} + +static void tcp_stream_on_snd_una_update(struct tcp_stream *tcp, u32 tx_bytes) +{ + show_block_marker(tcp->tx_packets); + if (tx_bytes == image_save_size) + tcp_stream_close(tcp); +} + +static void tcp_stream_on_established(struct tcp_stream *tcp) +{ + netcat_loop_state = NETLOOP_SUCCESS; +} + +static int tcp_stream_rx(struct tcp_stream *tcp, u32 rx_offs, void *buf, int len) +{ + void *ptr; + + ptr = map_sysmem(image_load_addr + rx_offs, len); + memcpy(ptr, buf, len); + unmap_sysmem(ptr); + + return len; +} + +static int tcp_stream_tx(struct tcp_stream *tcp, u32 tx_offs, void *buf, int maxlen) +{ + void *ptr; + + if (tx_offs + maxlen > image_save_size) + maxlen = image_save_size - tx_offs; + if (!maxlen) + return 0; + + ptr = map_sysmem(image_save_addr + tx_offs, maxlen); + memcpy(buf, ptr, maxlen); + unmap_sysmem(ptr); + + return maxlen; +} + +/* + * This function will be called on new connections (incoming or outgoing) + * It MUST: + * -- setup required tcp_stream callbacks (if connection will be accepted) + * -- return a connection verdict: + * 0: discard connection + * 1: accept connection + */ +static int tcp_stream_on_create(struct tcp_stream *tcp) +{ + if (listen) { + /* + * We are listening for incoming connections. + * Accept connections to netcat listen port only. + */ + if (tcp->lport != local_port) + return 0; + } else { + /* + * We are connecting to remote host. + * Check remote IP:port to make sure that this is our connection + */ + if (tcp->rhost.s_addr != server_ip.s_addr || + tcp->rport != server_port) + return 0; + } + + netcat_loop_state = NETLOOP_FAIL; + net_boot_file_size = 0; + marker = 0; + marks_in_line = 0; + + tcp->on_closed = tcp_stream_on_closed; + tcp->on_established = tcp_stream_on_established; + if (reading) { + tcp->on_rcv_nxt_update = tcp_stream_on_rcv_nxt_update; + tcp->rx = tcp_stream_rx; + } else { + tcp->on_snd_una_update = tcp_stream_on_snd_una_update; + tcp->tx = tcp_stream_tx; + } + + return 1; +} + +static void netcat_start(void) +{ + struct tcp_stream *tcp; + + memset(net_server_ethaddr, 0, 6); + tcp_stream_set_on_create_handler(tcp_stream_on_create); + + if (!strchr(net_boot_file_name, ':')) { + listen = 1; + printf("Listening on port %s...\n", net_boot_file_name); + + local_port = dectoul(net_boot_file_name, NULL); + } else { + listen = 0; + printf("Connecting to %s...\n", net_boot_file_name); + + server_ip = string_to_ip(net_boot_file_name); + server_port = dectoul(strchr(net_boot_file_name, ':') + 1, NULL); + + tcp = tcp_stream_connect(server_ip, server_port); + if (!tcp) { + printf("No free tcp streams\n"); + net_set_state(NETLOOP_FAIL); + return; + } + tcp_stream_put(tcp); + } +} + +void netcat_load_start(void) +{ + reading = 1; + + return netcat_start(); +} + +void netcat_save_start(void) +{ + reading = 0; + + return netcat_start(); +}