[PATCH 10/17] net: tftp: Add IPv6 support for tftpboot

Viacheslav Mitrofanov v.v.mitrofanov at yadro.com
Tue Aug 30 14:30:57 CEST 2022


The command tftpboot uses IPv4 by default. Add the possibility to use IPv6
instead. If an address in the command is an IPv6 address it will use IPv6
to boot or if there is a suffix -ipv6 in the end of the command it also
force using IPv6. All other tftpboot features and parameters are left
the same.

Signed-off-by: Viacheslav Mitrofanov <v.v.mitrofanov at yadro.com>
---
 cmd/net.c  | 22 +++++++++++++++++++
 net/net.c  | 34 ++++++++++++++++++++++++++++--
 net/tftp.c | 62 +++++++++++++++++++++++++++++++++++++++++++++++-------
 3 files changed, 108 insertions(+), 10 deletions(-)

diff --git a/cmd/net.c b/cmd/net.c
index 3619c843d8..0225f9ce3e 100644
--- a/cmd/net.c
+++ b/cmd/net.c
@@ -14,6 +14,7 @@
 #include <env.h>
 #include <image.h>
 #include <net.h>
+#include <net6.h>
 #include <net/udp.h>
 #include <net/sntp.h>
 
@@ -44,12 +45,22 @@ int do_tftpb(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[])
 	return ret;
 }
 
+#if IS_ENABLED(CONFIG_IPV6)
+U_BOOT_CMD(
+	tftpboot,	4,	1,	do_tftpb,
+	"boot image via network using TFTP protocol\n"
+	"To use IPv6 add -ipv6 parameter or use IPv6 hostIPaddr framed "
+	"with [] brackets",
+	"[loadAddress] [[hostIPaddr:]bootfilename] [" USE_IP6_CMD_PARAM "]"
+);
+#else
 U_BOOT_CMD(
 	tftpboot,	3,	1,	do_tftpb,
 	"boot image via network using TFTP protocol",
 	"[loadAddress] [[hostIPaddr:]bootfilename]"
 );
 #endif
+#endif
 
 #ifdef CONFIG_CMD_TFTPPUT
 static int do_tftpput(struct cmd_tbl *cmdtp, int flag, int argc,
@@ -205,6 +216,17 @@ static int netboot_common(enum proto_t proto, struct cmd_tbl *cmdtp, int argc,
 	if (s != NULL)
 		image_load_addr = hextoul(s, NULL);
 
+	if (IS_ENABLED(CONFIG_IPV6)) {
+		use_ip6 = false;
+
+		/* IPv6 parameter has to be always *last* */
+		if (!strcmp(argv[argc - 1], USE_IP6_CMD_PARAM)) {
+			use_ip6 = true;
+			/* It is a hack not to break switch/case code */
+			--argc;
+		}
+	}
+
 	switch (argc) {
 	case 1:
 		/* refresh bootfile name from env */
diff --git a/net/net.c b/net/net.c
index f818170930..77f42d1d59 100644
--- a/net/net.c
+++ b/net/net.c
@@ -449,6 +449,23 @@ restart:
 	debug_cond(DEBUG_INT_STATE, "--- net_loop Init\n");
 	net_init_loop();
 
+	/* Find out what protocol to use */
+	if (IS_ENABLED(CONFIG_IPV6) && !use_ip6) {
+		char *s, *e;
+		char str_ip6[16];
+
+		s = strchr(net_boot_file_name, '[');
+		e = strchr(net_boot_file_name, ']');
+		if (s && e) {
+			size_t len = e - s;
+
+			memcpy(str_ip6, s + 1, len);
+			str_ip6[len - 1] = '\0';
+			if (!string_to_ip6(str_ip6, &net_server_ip6))
+				use_ip6 = true;
+		}
+	}
+
 	switch (net_check_prereq(protocol)) {
 	case 1:
 		/* network not configured */
@@ -1381,7 +1398,14 @@ static int net_check_prereq(enum proto_t protocol)
 		/* Fall through */
 	case TFTPGET:
 	case TFTPPUT:
-		if (net_server_ip.s_addr == 0 && !is_serverip_in_cmd()) {
+		if (IS_ENABLED(CONFIG_IPV6) && use_ip6) {
+			if (!memcmp(&net_server_ip6, &net_null_addr_ip6,
+				    sizeof(struct in6_addr)) &&
+				    !strchr(net_boot_file_name, '[')) {
+				puts("*** ERROR: `serverip6' not set\n");
+				return 1;
+			}
+		} else if (net_server_ip.s_addr == 0 && !is_serverip_in_cmd()) {
 			puts("*** ERROR: `serverip' not set\n");
 			return 1;
 		}
@@ -1394,7 +1418,13 @@ common:
 	case NETCONS:
 	case FASTBOOT:
 	case TFTPSRV:
-		if (net_ip.s_addr == 0) {
+		if (IS_ENABLED(CONFIG_IPV6) && use_ip6) {
+			if (!memcmp(&net_link_local_ip6, &net_null_addr_ip6,
+				    sizeof(struct in6_addr))) {
+				puts("*** ERROR: `ip6addr` not set\n");
+				return 1;
+			}
+		} else if (net_ip.s_addr == 0) {
 			puts("*** ERROR: `ipaddr' not set\n");
 			return 1;
 		}
diff --git a/net/tftp.c b/net/tftp.c
index dea9c25ffd..52ff1a846d 100644
--- a/net/tftp.c
+++ b/net/tftp.c
@@ -15,6 +15,7 @@
 #include <log.h>
 #include <mapmem.h>
 #include <net.h>
+#include <net6.h>
 #include <asm/global_data.h>
 #include <net/tftp.h>
 #include "bootp.h"
@@ -41,6 +42,7 @@ DECLARE_GLOBAL_DATA_PTR;
 static ulong timeout_ms = TIMEOUT;
 static int timeout_count_max = (CONFIG_NET_RETRY_COUNT * 2);
 static ulong time_start;   /* Record time we started tftp */
+static struct in6_addr tftp_remote_ip6;
 
 /*
  * These globals govern the timeout behavior when attempting a connection to a
@@ -116,6 +118,7 @@ static int	tftp_put_final_block_sent;
 
 /* default TFTP block size */
 #define TFTP_BLOCK_SIZE		512
+#define TFTP_MTU_BLOCKSIZE6 (CONFIG_TFTP_BLOCKSIZE - 20)
 /* sequence number is 16 bit */
 #define TFTP_SEQUENCE_SIZE	((ulong)(1<<16))
 
@@ -320,7 +323,11 @@ static void tftp_send(void)
 	 *	We will always be sending some sort of packet, so
 	 *	cobble together the packet headers now.
 	 */
-	pkt = net_tx_packet + net_eth_hdr_size() + IP_UDP_HDR_SIZE;
+	if (IS_ENABLED(CONFIG_IPV6) && use_ip6)
+		pkt = net_tx_packet + net_eth_hdr_size() +
+		      IP6_HDR_SIZE + UDP_HDR_SIZE;
+	else
+		pkt = net_tx_packet + net_eth_hdr_size() + IP_UDP_HDR_SIZE;
 
 	switch (tftp_state) {
 	case STATE_SEND_RRQ:
@@ -422,8 +429,14 @@ static void tftp_send(void)
 		break;
 	}
 
-	net_send_udp_packet(net_server_ethaddr, tftp_remote_ip,
-			    tftp_remote_port, tftp_our_port, len);
+	if (IS_ENABLED(CONFIG_IPV6) && use_ip6)
+		net_send_udp_packet6(net_server_ethaddr,
+				     &tftp_remote_ip6,
+				     tftp_remote_port,
+				     tftp_our_port, len);
+	else
+		net_send_udp_packet(net_server_ethaddr, tftp_remote_ip,
+				    tftp_remote_port, tftp_our_port, len);
 
 	if (err_pkt)
 		net_set_state(NETLOOP_FAIL);
@@ -750,6 +763,9 @@ void tftp_start(enum proto_t protocol)
 	debug("TFTP blocksize = %i, TFTP windowsize = %d timeout = %ld ms\n",
 	      tftp_block_size_option, tftp_window_size_option, timeout_ms);
 
+	if (IS_ENABLED(CONFIG_IPV6))
+		tftp_remote_ip6 = net_server_ip6;
+
 	tftp_remote_ip = net_server_ip;
 	if (!net_parse_bootfile(&tftp_remote_ip, tftp_filename, MAX_LEN)) {
 		sprintf(default_filename, "%02X%02X%02X%02X.img",
@@ -764,18 +780,48 @@ void tftp_start(enum proto_t protocol)
 		printf("*** Warning: no boot file name; using '%s'\n",
 		       tftp_filename);
 	}
+	if (IS_ENABLED(CONFIG_IPV6)) {
+		if (use_ip6) {
+			char *s, *e;
+
+			s = strchr(net_boot_file_name, '[');
+			e = strchr(net_boot_file_name, ']');
+			if (s && e) {
+				*e++ = 0;
+				string_to_ip6(s + 1, &tftp_remote_ip6);
+				strlcpy(tftp_filename, e + 1, MAX_LEN);
+			} else {
+				strlcpy(tftp_filename, net_boot_file_name, MAX_LEN);
+				tftp_filename[MAX_LEN - 1] = 0;
+			}
+		}
+	}
 
 	printf("Using %s device\n", eth_get_name());
-	printf("TFTP %s server %pI4; our IP address is %pI4",
+
+	if (IS_ENABLED(CONFIG_IPV6) && use_ip6) {
+		printf("TFTP from server %pI6c; our IP address is %pI6c",
+		       &tftp_remote_ip6, &net_ip6);
+
+		if (tftp_block_size_option > TFTP_MTU_BLOCKSIZE6)
+			tftp_block_size_option = TFTP_MTU_BLOCKSIZE6;
+	} else {
+		printf("TFTP %s server %pI4; our IP address is %pI4",
 #ifdef CONFIG_CMD_TFTPPUT
-	       protocol == TFTPPUT ? "to" : "from",
+		       protocol == TFTPPUT ? "to" : "from",
 #else
-	       "from",
+		       "from",
 #endif
-	       &tftp_remote_ip, &net_ip);
+		       &tftp_remote_ip, &net_ip);
+	}
 
 	/* Check if we need to send across this subnet */
-	if (net_gateway.s_addr && net_netmask.s_addr) {
+	if (IS_ENABLED(CONFIG_IPV6) && use_ip6) {
+		if (!ip6_addr_in_subnet(&net_ip6, &tftp_remote_ip6,
+					net_prefix_length))
+			printf("; sending through gateway %pI6c",
+			       &net_gateway6);
+	} else if (net_gateway.s_addr && net_netmask.s_addr) {
 		struct in_addr our_net;
 		struct in_addr remote_net;
 
-- 
2.25.1



More information about the U-Boot mailing list