[PATCH] net/netcat: add netcat over tcp support for legacy ip stack
Mikhail Kshevetskiy
mikhail.kshevetskiy at iopsys.eu
Thu Nov 14 04:42:42 CET 2024
This patch adds downloading/uploading of data with netcat.
Client/server mode both supported. It can be treated as
an example of new legacy ip 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 at iopsys.eu>
Reviewed-by: Simon Glass <sjg at 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 b2d0348fe30..18f8880b5d9 100644
--- a/cmd/Kconfig
+++ b/cmd/Kconfig
@@ -2077,6 +2077,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 c90578e1b9f..3dc9918207e 100644
--- a/cmd/net.c
+++ b/cmd/net.c
@@ -206,6 +206,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];
@@ -323,16 +346,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:
@@ -354,7 +378,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 {
@@ -365,7 +390,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 d6902508ebd..fdb6d2abb85 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 */
extern bool net_boot_file_name_explicit;
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 at 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 209377aeb26..4cf13efe17c 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_CMD_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 39bc3ce8903..50e8cfa9ff2 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 at 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();
+}
--
2.45.2
More information about the U-Boot
mailing list