[U-Boot] [RFC PATCH v3 08/11] net: TFTP over IPv6
Chris Packham
judge.packham at gmail.com
Wed Jan 25 10:56:19 CET 2017
Add support for UDP/TFTP over IPv6. To support specifying an server IPv6
address in the command square brackets must be used to separate the
address from the filename. e.g
tftpboot6 [2001:db8::1]:zImage
Signed-off-by: Chris Packham <judge.packham at gmail.com>
---
Changes in v3: None
Changes in v2:
- Support parsing the server address from the command parameter.
cmd/Kconfig | 9 +++++++++
cmd/net.c | 13 ++++++++++++
include/net.h | 2 +-
include/net6.h | 4 ++++
net/net.c | 3 +++
net/net6.c | 64 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
net/tftp.c | 58 ++++++++++++++++++++++++++++++++++++++++++++++++++++
7 files changed, 152 insertions(+), 1 deletion(-)
diff --git a/cmd/Kconfig b/cmd/Kconfig
index 37126577bc65..473d354fcb85 100644
--- a/cmd/Kconfig
+++ b/cmd/Kconfig
@@ -504,6 +504,15 @@ config CMD_NET
bootp - boot image via network using BOOTP/TFTP protocol
tftpboot - boot image via network using TFTP protocol
+config CMD_NET6
+ bool "ipv6 commands"
+ select NET
+ select NET6
+ default n
+ help
+ IPv6 network commands
+ tftpboot6 - boot image via network using TFTP protocol
+
config CMD_TFTPPUT
bool "tftp put"
help
diff --git a/cmd/net.c b/cmd/net.c
index 7f40f257c03c..e2c295eabb17 100644
--- a/cmd/net.c
+++ b/cmd/net.c
@@ -42,6 +42,19 @@ U_BOOT_CMD(
"[loadAddress] [[hostIPaddr:]bootfilename]"
);
+#ifdef CONFIG_CMD_NET6
+int do_tftpb6(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])
+{
+ return netboot_common(TFTP6, cmdtp, argc, argv);
+}
+
+U_BOOT_CMD(
+ tftpboot6, 3, 1, do_tftpb6,
+ "boot image via network using TFTP protocol",
+ "[loadAddress] [[hostIP6Addr]:][bootfilename]"
+);
+#endif
+
#ifdef CONFIG_CMD_TFTPPUT
int do_tftpput(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])
{
diff --git a/include/net.h b/include/net.h
index be75c6c65c6b..6c968711c37f 100644
--- a/include/net.h
+++ b/include/net.h
@@ -546,7 +546,7 @@ extern int net_restart_wrap; /* Tried all network devices */
enum proto_t {
BOOTP, RARP, ARP, TFTPGET, DHCP, PING, PING6, DNS, NFS, CDP, NETCONS,
- SNTP, TFTPSRV, TFTPPUT, LINKLOCAL
+ SNTP, TFTPSRV, TFTPPUT, TFTP6, LINKLOCAL
};
extern char net_boot_file_name[1024];/* Boot File name */
diff --git a/include/net6.h b/include/net6.h
index ff97c39a6925..7ae177738e86 100644
--- a/include/net6.h
+++ b/include/net6.h
@@ -246,6 +246,10 @@ void ping6_start(void);
void ping6_receive(struct ethernet_hdr *et, struct ip6_hdr *ip6,
int len);
+/* Transmit UDP packet using IPv6, performing neighbour discovery if needed */
+int net_send_udp_packet6(uchar *ether, struct in6_addr *dest,
+ int dport, int sport, int len);
+
/* handler for incoming IPv6 echo packet */
void net_ip6_handler(struct ethernet_hdr *et, struct ip6_hdr *ip6,
int len);
diff --git a/net/net.c b/net/net.c
index 527c99f96f9f..567550ef09b6 100644
--- a/net/net.c
+++ b/net/net.c
@@ -454,6 +454,9 @@ restart:
#ifdef CONFIG_CMD_TFTPPUT
case TFTPPUT:
#endif
+#ifdef CONFIG_CMD_NET6
+ case TFTP6:
+#endif
/* always use ARP to get server ethernet address */
tftp_start(protocol);
break;
diff --git a/net/net6.c b/net/net6.c
index 8f0c7214f8e1..5b8a003f1c4a 100644
--- a/net/net6.c
+++ b/net/net6.c
@@ -342,6 +342,50 @@ ip6_add_hdr(uchar *xip, struct in6_addr *src, struct in6_addr *dest,
return sizeof(struct ip6_hdr);
}
+int
+net_send_udp_packet6(uchar *ether, struct in6_addr *dest, int dport, int sport, int len)
+{
+ uchar *pkt;
+ struct udp_hdr *udp;
+
+ udp = (struct udp_hdr *)((uchar *)net_tx_packet + net_eth_hdr_size() + IP6_HDR_SIZE);
+
+ udp->udp_dst = htons(dport);
+ udp->udp_src = htons(sport);
+ udp->udp_len = htons(len + UDP_HDR_SIZE);
+ /* checksum */
+ udp->udp_xsum = 0;
+ udp->udp_xsum = csum_ipv6_magic(&net_ip6, dest, len + UDP_HDR_SIZE,
+ IPPROTO_UDP, csum_partial((__u8 *)udp, len + UDP_HDR_SIZE, 0));
+
+ /* if MAC address was not discovered yet, save the packet and do neighbour discovery */
+ if (memcmp(ether, net_null_ethaddr, 6) == 0) {
+ net_copy_ip6(&net_nd_sol_packet_ip6, dest);
+ net_nd_packet_mac = ether;
+
+ pkt = net_nd_tx_packet;
+ pkt += net_set_ether(pkt, net_nd_packet_mac, PROT_IP6);
+ pkt += ip6_add_hdr(pkt, &net_ip6, dest, IPPROTO_UDP, 64, len + UDP_HDR_SIZE);
+ memcpy(pkt, (uchar *)udp, len + UDP_HDR_SIZE);
+
+ /* size of the waiting packet */
+ net_nd_tx_packet_size = (pkt - net_nd_tx_packet) + UDP_HDR_SIZE + len;
+
+ /* and do the neighbor solicitation */
+ net_nd_try = 1;
+ net_nd_timer_start = get_timer(0);
+ ndisc_request();
+ return 1; /* waiting */
+ }
+
+ pkt = (uchar *)net_tx_packet;
+ pkt += net_set_ether(pkt, ether, PROT_IP6);
+ pkt += ip6_add_hdr(pkt, &net_ip6, dest, IPPROTO_UDP, 64, len + UDP_HDR_SIZE);
+ (void) eth_send(net_tx_packet, (pkt - net_tx_packet) + UDP_HDR_SIZE + len);
+
+ return 0; /* transmitted */
+}
+
void net_ip6_handler(struct ethernet_hdr *et, struct ip6_hdr *ip6, int len)
{
struct in_addr zero_ip = {.s_addr = 0 };
@@ -388,6 +432,26 @@ void net_ip6_handler(struct ethernet_hdr *et, struct ip6_hdr *ip6, int len)
}
break;
+ case IPPROTO_UDP:
+ udp = (struct udp_hdr *)(((uchar *)ip6) + IP6_HDR_SIZE);
+ csum = udp->udp_xsum;
+ hlen = ntohs(ip6->payload_len);
+ udp->udp_xsum = 0;
+ /* checksum */
+ udp->udp_xsum = csum_ipv6_magic(&ip6->saddr, &ip6->daddr,
+ hlen, IPPROTO_UDP, csum_partial((__u8 *)udp, hlen, 0));
+ if (csum != udp->udp_xsum)
+ return;
+
+ /* IP header OK. Pass the packet to the current handler. */
+ net_get_udp_handler()((uchar *)ip6 + IP6_HDR_SIZE +
+ UDP_HDR_SIZE,
+ ntohs(udp->udp_dst),
+ zero_ip,
+ ntohs(udp->udp_src),
+ ntohs(udp->udp_len) - 8);
+ break;
+
default:
return;
break;
diff --git a/net/tftp.c b/net/tftp.c
index ced45ec1f1f4..dbcefd2d4257 100644
--- a/net/tftp.c
+++ b/net/tftp.c
@@ -11,6 +11,7 @@
#include <efi_loader.h>
#include <mapmem.h>
#include <net.h>
+#include <net6.h>
#include <net/tftp.h>
#include "bootp.h"
#ifdef CONFIG_SYS_DIRECT_FLASH_TFTP
@@ -67,6 +68,9 @@ enum {
};
static struct in_addr tftp_remote_ip;
+#ifdef CONFIG_CMD_NET6
+static struct in6_addr tftp_remote_ip6;
+#endif
/* The UDP port at their end */
static int tftp_remote_port;
/* The UDP port at our end */
@@ -95,6 +99,10 @@ static int tftp_put_final_block_sent;
#else
#define tftp_put_active 0
#endif
+#ifdef CONFIG_CMD_NET6
+/* 1 if using IPv6, else 0 */
+static int tftp6_active;
+#endif
#define STATE_SEND_RRQ 1
#define STATE_DATA 2
@@ -130,6 +138,8 @@ static char tftp_filename[MAX_LEN];
#else
#define TFTP_MTU_BLOCKSIZE 1468
#endif
+/* IPv6 adds 20 bytes extra overhead */
+#define TFTP_MTU_BLOCKSIZE6 (TFTP_MTU_BLOCKSIZE - 20)
static unsigned short tftp_block_size = TFTP_BLOCK_SIZE;
static unsigned short tftp_block_size_option = TFTP_MTU_BLOCKSIZE;
@@ -342,6 +352,12 @@ static void tftp_send(void)
* We will always be sending some sort of packet, so
* cobble together the packet headers now.
*/
+#ifdef CONFIG_CMD_NET6
+ if (tftp6_active)
+ pkt = net_tx_packet + net_eth_hdr_size() +
+ IP6_HDR_SIZE + UDP_HDR_SIZE;
+ else
+#endif
pkt = net_tx_packet + net_eth_hdr_size() + IP_UDP_HDR_SIZE;
switch (tftp_state) {
@@ -441,6 +457,12 @@ static void tftp_send(void)
break;
}
+#ifdef CONFIG_CMD_NET6
+ if (tftp6_active)
+ net_send_udp_packet6(net_server_ethaddr, &tftp_remote_ip6,
+ tftp_remote_port, tftp_our_port, len);
+ else
+#endif
net_send_udp_packet(net_server_ethaddr, tftp_remote_ip,
tftp_remote_port, tftp_our_port, len);
}
@@ -734,6 +756,10 @@ void tftp_start(enum proto_t protocol)
debug("TFTP blocksize = %i, timeout = %ld ms\n",
tftp_block_size_option, timeout_ms);
+#ifdef CONFIG_CMD_NET6
+ tftp6_active = (protocol == TFTP6);
+ tftp_remote_ip6 = net_server_ip6;
+#endif
tftp_remote_ip = net_server_ip;
if (net_boot_file_name[0] == '\0') {
sprintf(default_filename, "%02X%02X%02X%02X.img",
@@ -747,6 +773,20 @@ void tftp_start(enum proto_t protocol)
printf("*** Warning: no boot file name; using '%s'\n",
tftp_filename);
+#ifdef CONFIG_CMD_NET6
+ } else if (tftp6_active) {
+ 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);
+ strncpy(tftp_filename, e + 1, MAX_LEN);
+ } else {
+ strncpy(tftp_filename, net_boot_file_name, MAX_LEN);
+ tftp_filename[MAX_LEN - 1] = 0;
+ }
+#endif
} else {
char *p = strchr(net_boot_file_name, ':');
@@ -761,6 +801,15 @@ void tftp_start(enum proto_t protocol)
}
printf("Using %s device\n", eth_get_name());
+#ifdef CONFIG_CMD_NET6
+ if (tftp6_active) {
+ 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
+#endif
printf("TFTP %s server %pI4; our IP address is %pI4",
#ifdef CONFIG_CMD_TFTPPUT
protocol == TFTPPUT ? "to" : "from",
@@ -770,6 +819,15 @@ void tftp_start(enum proto_t protocol)
&tftp_remote_ip, &net_ip);
/* Check if we need to send across this subnet */
+#ifdef CONFIG_CMD_NET6
+ if (tftp6_active) {
+ if (!ip6_addr_in_subnet(&net_ip6, &tftp_remote_ip6,
+ net_prefix_length)) {
+ printf("; sending through gateway %pI6c",
+ &net_gateway6);
+ }
+ } else
+#endif
if (net_gateway.s_addr && net_netmask.s_addr) {
struct in_addr our_net;
struct in_addr remote_net;
--
2.11.0.24.ge6920cf
More information about the U-Boot
mailing list