All of lore.kernel.org
 help / color / mirror / Atom feed
* [U-Boot] [RFC PATCH 0/8] IPv6 support
@ 2015-10-12  7:43 Chris Packham
  2015-10-12  7:43 ` [U-Boot] [RFC PATCH 1/8] Initial net6.h Chris Packham
                   ` (9 more replies)
  0 siblings, 10 replies; 31+ messages in thread
From: Chris Packham @ 2015-10-12  7:43 UTC (permalink / raw)
  To: u-boot

This series adds basic IPv6 support to U-boot. It is a reboot of my
earlier work on this[1]. Technically this is v4 of the series but with
the amount of time that has passed, rebasing and running though
checkpatch as well as the support for TFTP I've decided to reset the
counter back to 1.

Most of this is ported from Allied Telesis' additions to u-boot[2].
(Note that I am employed by Allied Telesis[3]). The hard work was done
some time ago by Angga, I've cleaned it up and made some improvements
with the hope of getting it accepted upstream.

A few open issues/questions

1) Presumably the majority of the actual V6 code would be included by a
config option. How far should I take that? Should the vsprintf code be
conditional? I'm also not sure where to draw the line between
CONFIG_NET6 and CONFIG_CMD_NET6, CONFIG_NET and CONFIG_CMD_NET look
similarly confused.

2) This code parallels net.c and net.h. Should I
continue this for the final version or integrate it into net.[ch].

3) rxhand_f currently takes an struct in_addr *. TFTP doesn't use this
(I haven't looked at other users). To support V6 this may need to be a
new union, a void * with some kind of flag or nothing if no rxhandler
actually cares.

Regards,
Chris
--
[1] - http://thread.gmane.org/gmane.comp.boot-loaders.u-boot/151390
[2] - http://www.alliedtelesis.co.nz/support/gpl/other.html
[3] - some of this has been done on work time, other parts have been
      done in my personal time. Since I'm subscribed to the list using
      my gmail account I've signed using that.



Chris Packham (8):
  Initial net6.h
  lib: vsprintf: add IPv6 compressed format %pI6c
  lib: net_utils: make string_to_ip stricter
  lib: net_utils: add string_to_ip6
  net: ipv6 support
  net: TFTP over IPv6
  net: IPv6 documentation
  net: e1000 enable multicast reception

 README                 |   3 +
 common/Kconfig         |  15 ++
 common/cmd_net.c       |  41 +++++
 doc/README.ipv6        |  34 ++++
 drivers/net/e1000.c    |   5 +
 include/env_callback.h |   9 +
 include/env_flags.h    |  10 ++
 include/net.h          |   5 +-
 include/net6.h         | 280 +++++++++++++++++++++++++++++++
 lib/net_utils.c        | 132 ++++++++++++++-
 lib/vsprintf.c         | 144 +++++++++++++---
 net/Kconfig            |   5 +
 net/Makefile           |   3 +
 net/ndisc.c            | 269 ++++++++++++++++++++++++++++++
 net/ndisc.h            |  27 +++
 net/net.c              |  39 ++++-
 net/net6.c             | 439 +++++++++++++++++++++++++++++++++++++++++++++++++
 net/ping6.c            | 111 +++++++++++++
 net/tftp.c             |  37 +++++
 19 files changed, 1583 insertions(+), 25 deletions(-)
 create mode 100644 doc/README.ipv6
 create mode 100644 include/net6.h
 create mode 100644 net/ndisc.c
 create mode 100644 net/ndisc.h
 create mode 100644 net/net6.c
 create mode 100644 net/ping6.c

-- 
2.5.3

^ permalink raw reply	[flat|nested] 31+ messages in thread

* [U-Boot] [RFC PATCH 1/8] Initial net6.h
  2015-10-12  7:43 [U-Boot] [RFC PATCH 0/8] IPv6 support Chris Packham
@ 2015-10-12  7:43 ` Chris Packham
  2015-11-02 20:32   ` Joe Hershberger
  2015-10-12  7:43 ` [U-Boot] [RFC PATCH 2/8] lib: vsprintf: add IPv6 compressed format %pI6c Chris Packham
                   ` (8 subsequent siblings)
  9 siblings, 1 reply; 31+ messages in thread
From: Chris Packham @ 2015-10-12  7:43 UTC (permalink / raw)
  To: u-boot

The initial net6.h just has the definition of an IPv6 address and IPv6
header. Subsequent changes will build on this.

Signed-off-by: Chris Packham <judge.packham@gmail.com>
---
It may make sense to include this in net.h but for now I haven't done
so.

 include/net6.h | 48 ++++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 48 insertions(+)
 create mode 100644 include/net6.h

diff --git a/include/net6.h b/include/net6.h
new file mode 100644
index 0000000..b622951
--- /dev/null
+++ b/include/net6.h
@@ -0,0 +1,48 @@
+/**
+ * Simple IPv6 network layer implementation.
+ *
+ * Based and/or adapted from the IPv4 network layer in net.[hc]
+ *
+ * (C) Copyright 2013 Allied Telesis Labs NZ
+ *
+ * SPDX-License-Identifier:	GPL-2.0+
+ */
+#ifndef __NET6_H__
+#define __NET6_H__
+
+struct in6_addr {
+	union {
+		__u8	u6_addr8[16];
+		__be16	u6_addr16[8];
+		__be32	u6_addr32[4];
+	} in6_u;
+
+#define s6_addr		in6_u.u6_addr8
+#define s6_addr16	in6_u.u6_addr16
+#define s6_addr32	in6_u.u6_addr32
+};
+
+/**
+ * struct ipv6hdr - Internet Protocol V6 (IPv6) header.
+ *
+ * IPv6 packet header as defined in RFC 2460.
+ */
+struct ip6_hdr {
+#if defined(__LITTLE_ENDIAN_BITFIELD)
+	__u8    priority:4,
+		version:4;
+#elif defined(__BIG_ENDIAN_BITFIELD)
+	__u8    version:4,
+		priority:4;
+#else
+#error  "Please fix <asm/byteorder.h>"
+#endif
+	__u8		flow_lbl[3];
+	__be16		payload_len;
+	__u8		nexthdr;
+	__u8		hop_limit;
+	struct in6_addr	saddr;
+	struct in6_addr	daddr;
+};
+
+#endif /* __NET6_H__ */
-- 
2.5.3

^ permalink raw reply related	[flat|nested] 31+ messages in thread

* [U-Boot] [RFC PATCH 2/8] lib: vsprintf: add IPv6 compressed format %pI6c
  2015-10-12  7:43 [U-Boot] [RFC PATCH 0/8] IPv6 support Chris Packham
  2015-10-12  7:43 ` [U-Boot] [RFC PATCH 1/8] Initial net6.h Chris Packham
@ 2015-10-12  7:43 ` Chris Packham
  2015-11-02 20:42   ` Joe Hershberger
  2015-10-12  7:43 ` [U-Boot] [RFC PATCH 3/8] lib: net_utils: make string_to_ip stricter Chris Packham
                   ` (7 subsequent siblings)
  9 siblings, 1 reply; 31+ messages in thread
From: Chris Packham @ 2015-10-12  7:43 UTC (permalink / raw)
  To: u-boot

Add support for "human friendly" IPv6 address representations as
specified in
http://tools.ietf.org/html/draft-ietf-6man-text-addr-representation-00

This code has been adapted from Linux kernel with minimal modification.

Signed-off-by: Chris Packham <judge.packham@gmail.com>
---

 include/net6.h |  13 ++++++
 lib/vsprintf.c | 144 +++++++++++++++++++++++++++++++++++++++++++++++++--------
 2 files changed, 137 insertions(+), 20 deletions(-)

diff --git a/include/net6.h b/include/net6.h
index b622951..1b82c25 100644
--- a/include/net6.h
+++ b/include/net6.h
@@ -45,4 +45,17 @@ struct ip6_hdr {
 	struct in6_addr	daddr;
 };
 
+/* ::ffff:0:0/96 is reserved for v4 mapped addresses */
+static inline int ipv6_addr_v4mapped(const struct in6_addr *a)
+{
+	return (a->s6_addr32[0] | a->s6_addr32[1] |
+		(a->s6_addr32[2] ^ htonl(0x0000ffff))) == 0;
+}
+
+/* Intra-Site Automatic Tunnel Addressing Protocol Address */
+static inline int ipv6_addr_is_isatap(const struct in6_addr *a)
+{
+	return (a->s6_addr32[2] | htonl(0x02000000)) == htonl(0x02005EFE);
+}
+
 #endif /* __NET6_H__ */
diff --git a/lib/vsprintf.c b/lib/vsprintf.c
index 4c82837..349234a 100644
--- a/lib/vsprintf.c
+++ b/lib/vsprintf.c
@@ -23,6 +23,7 @@
 #endif
 
 #include <div64.h>
+#include <net6.h>
 #define noinline __attribute__((noinline))
 
 unsigned long simple_strtoul(const char *cp, char **endp,
@@ -304,6 +305,7 @@ static noinline char *put_dec(char *buf, uint64_t num)
 #define LEFT	16		/* left justified */
 #define SMALL	32		/* Must be 32 == 0x20 */
 #define SPECIAL	64		/* 0x */
+#define COMPRESSED 128		/* use compressed format */
 
 #ifdef CONFIG_SYS_VSNPRINTF
 /*
@@ -469,12 +471,108 @@ static char *mac_address_string(char *buf, char *end, u8 *addr, int field_width,
 		      flags & ~SPECIAL);
 }
 
-static char *ip6_addr_string(char *buf, char *end, u8 *addr, int field_width,
-			 int precision, int flags)
+static char *ip4_string(char *p, u8 *addr)
+{
+	char temp[3];	/* hold each IP quad in reverse order */
+	int i, digits;
+
+	for (i = 0; i < 4; i++) {
+		digits = put_dec_trunc(temp, addr[i]) - temp;
+		/* reverse the digits in the quad */
+		while (digits--)
+			*p++ = temp[digits];
+		if (i != 3)
+			*p++ = '.';
+	}
+	*p = '\0';
+
+	return p;
+}
+
+static char *ip6_compressed_string(char *p, u8 *addr)
+{
+	int i, j, range;
+	unsigned char zerolength[8];
+	int longest = 1;
+	int colonpos = -1;
+	u16 word;
+	u8 hi, lo;
+	int needcolon = 0;
+	int useIPv4;
+	struct in6_addr in6;
+
+	memcpy(&in6, addr, sizeof(struct in6_addr));
+
+	useIPv4 = ipv6_addr_v4mapped(&in6) || ipv6_addr_is_isatap(&in6);
+
+	memset(zerolength, 0, sizeof(zerolength));
+
+	if (useIPv4)
+		range = 6;
+	else
+		range = 8;
+
+	/* find position of longest 0 run */
+	for (i = 0; i < range; i++) {
+		for (j = i; j < range; j++) {
+			if (in6.s6_addr16[j] != 0)
+				break;
+			zerolength[i]++;
+		}
+	}
+	for (i = 0; i < range; i++) {
+		if (zerolength[i] > longest) {
+			longest = zerolength[i];
+			colonpos = i;
+		}
+	}
+	if (longest == 1)		/* don't compress a single 0 */
+		colonpos = -1;
+
+	/* emit address */
+	for (i = 0; i < range; i++) {
+		if (i == colonpos) {
+			if (needcolon || i == 0)
+				*p++ = ':';
+			*p++ = ':';
+			needcolon = 0;
+			i += longest - 1;
+			continue;
+		}
+		if (needcolon) {
+			*p++ = ':';
+			needcolon = 0;
+		}
+		/* hex u16 without leading 0s */
+		word = ntohs(in6.s6_addr16[i]);
+		hi = word >> 8;
+		lo = word & 0xff;
+		if (hi) {
+			if (hi > 0x0f)
+				p = pack_hex_byte(p, hi);
+			else
+				*p++ = hex_asc_lo(hi);
+			p = pack_hex_byte(p, lo);
+		} else if (lo > 0x0f) {
+			p = pack_hex_byte(p, lo);
+		} else {
+			*p++ = hex_asc_lo(lo);
+		}
+		needcolon = 1;
+	}
+
+	if (useIPv4) {
+		if (needcolon)
+			*p++ = ':';
+		p = ip4_string(p, &in6.s6_addr[12]);
+	}
+	*p = '\0';
+
+	return p;
+}
+
+static char *ip6_string(char *p, u8 *addr, int flags)
 {
-	/* (8 * 4 hex digits), 7 colons and trailing zero */
-	char ip6_addr[8 * 5];
-	char *p = ip6_addr;
 	int i;
 
 	for (i = 0; i < 8; i++) {
@@ -485,6 +583,19 @@ static char *ip6_addr_string(char *buf, char *end, u8 *addr, int field_width,
 	}
 	*p = '\0';
 
+	return p;
+}
+
+static char *ip6_addr_string(char *buf, char *end, u8 *addr, int field_width,
+			 int precision, int flags)
+{
+	char ip6_addr[sizeof("xxxx:xxxx:xxxx:xxxx:xxxx:xxxx:255.255.255.255")];
+
+	if (flags & COMPRESSED)
+		ip6_compressed_string(ip6_addr, addr);
+	else
+		ip6_string(ip6_addr, addr, flags);
+
 	return string(buf, end, ip6_addr, field_width, precision,
 		      flags & ~SPECIAL);
 }
@@ -492,21 +603,9 @@ static char *ip6_addr_string(char *buf, char *end, u8 *addr, int field_width,
 static char *ip4_addr_string(char *buf, char *end, u8 *addr, int field_width,
 			 int precision, int flags)
 {
-	/* (4 * 3 decimal digits), 3 dots and trailing zero */
-	char ip4_addr[4 * 4];
-	char temp[3];	/* hold each IP quad in reverse order */
-	char *p = ip4_addr;
-	int i, digits;
+	char ip4_addr[sizeof("255.255.255.255")];
 
-	for (i = 0; i < 4; i++) {
-		digits = put_dec_trunc(temp, addr[i]) - temp;
-		/* reverse the digits in the quad */
-		while (digits--)
-			*p++ = temp[digits];
-		if (i != 3)
-			*p++ = '.';
-	}
-	*p = '\0';
+	ip4_string(ip4_addr, addr);
 
 	return string(buf, end, ip4_addr, field_width, precision,
 		      flags & ~SPECIAL);
@@ -526,6 +625,8 @@ static char *ip4_addr_string(char *buf, char *end, u8 *addr, int field_width,
  *       decimal for v4 and colon separated network-order 16 bit hex for v6)
  * - 'i' [46] for 'raw' IPv4/IPv6 addresses, IPv6 omits the colons, IPv4 is
  *       currently the same
+ * - 'I6c' for IPv6 addresses printed as specified by
+ *       http://tools.ietf.org/html/rfc5952
  *
  * Note: The difference between 'S' and 'F' is that on ia64 and ppc64
  * function pointers are really function descriptors, which contain a
@@ -569,9 +670,12 @@ static char *pointer(const char *fmt, char *buf, char *end, void *ptr,
 		flags |= SPECIAL;
 		/* Fallthrough */
 	case 'I':
-		if (fmt[1] == '6')
+		if (fmt[1] == '6') {
+			if (fmt[2] == 'c')
+				flags |= COMPRESSED;
 			return ip6_addr_string(buf, end, ptr, field_width,
 					       precision, flags);
+		}
 		if (fmt[1] == '4')
 			return ip4_addr_string(buf, end, ptr, field_width,
 					       precision, flags);
-- 
2.5.3

^ permalink raw reply related	[flat|nested] 31+ messages in thread

* [U-Boot] [RFC PATCH 3/8] lib: net_utils: make string_to_ip stricter
  2015-10-12  7:43 [U-Boot] [RFC PATCH 0/8] IPv6 support Chris Packham
  2015-10-12  7:43 ` [U-Boot] [RFC PATCH 1/8] Initial net6.h Chris Packham
  2015-10-12  7:43 ` [U-Boot] [RFC PATCH 2/8] lib: vsprintf: add IPv6 compressed format %pI6c Chris Packham
@ 2015-10-12  7:43 ` Chris Packham
  2015-11-02 20:42   ` Joe Hershberger
  2015-11-09  2:49   ` Chris Packham
  2015-10-12  7:43 ` [U-Boot] [RFC PATCH 4/8] lib: net_utils: add string_to_ip6 Chris Packham
                   ` (6 subsequent siblings)
  9 siblings, 2 replies; 31+ messages in thread
From: Chris Packham @ 2015-10-12  7:43 UTC (permalink / raw)
  To: u-boot

Previously values greater than 255 were implicitly truncated. Add some
stricter checking to reject addresses with components >255.

With the input "1234192.168.1.1" the old behaviour would truncate the
address to 192.168.1.1. New behaviour rejects the string outright and
returns 0.0.0.0, which for the purposes of IP addresses can be
considered an error.

Signed-off-by: Chris Packham <judge.packham@gmail.com>
---

 lib/net_utils.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/lib/net_utils.c b/lib/net_utils.c
index cfae842..0fca54d 100644
--- a/lib/net_utils.c
+++ b/lib/net_utils.c
@@ -24,10 +24,19 @@ struct in_addr string_to_ip(const char *s)
 
 	for (addr.s_addr = 0, i = 0; i < 4; ++i) {
 		ulong val = s ? simple_strtoul(s, &e, 10) : 0;
+		if (val > 255) {
+			addr.s_addr = 0;
+			return addr;
+		}
 		addr.s_addr <<= 8;
 		addr.s_addr |= (val & 0xFF);
-		if (s) {
-			s = (*e) ? e+1 : e;
+		if (*e == '.') {
+			s = e + 1;
+		} else if (*e == '\0') {
+			break;
+		} else {
+			addr.s_addr = 0;
+			return addr;
 		}
 	}
 
-- 
2.5.3

^ permalink raw reply related	[flat|nested] 31+ messages in thread

* [U-Boot] [RFC PATCH 4/8] lib: net_utils: add string_to_ip6
  2015-10-12  7:43 [U-Boot] [RFC PATCH 0/8] IPv6 support Chris Packham
                   ` (2 preceding siblings ...)
  2015-10-12  7:43 ` [U-Boot] [RFC PATCH 3/8] lib: net_utils: make string_to_ip stricter Chris Packham
@ 2015-10-12  7:43 ` Chris Packham
  2015-11-02 20:43   ` Joe Hershberger
  2015-10-12  7:43 ` [U-Boot] [RFC PATCH 5/8] net: ipv6 support Chris Packham
                   ` (5 subsequent siblings)
  9 siblings, 1 reply; 31+ messages in thread
From: Chris Packham @ 2015-10-12  7:43 UTC (permalink / raw)
  To: u-boot

string_to_ip6 parses an IPv6 address from a string. Parsing v6 addresses
is a bit more complicated than parsing v4 because there are a number of
different formats that can be used.

Signed-off-by: Chris Packham <judge.packham@gmail.com>
---
I'm sure the parsing can be better and done in less code with only a
single pass but I haven't yet figured it out. The main problem is that
"::" can represent a variable number of contiguous "0000:" so when
parsing "::" we can't tell how many half words to skip.

 include/net6.h  |   3 ++
 lib/net_utils.c | 119 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 122 insertions(+)

diff --git a/include/net6.h b/include/net6.h
index 1b82c25..a41eb87 100644
--- a/include/net6.h
+++ b/include/net6.h
@@ -58,4 +58,7 @@ static inline int ipv6_addr_is_isatap(const struct in6_addr *a)
 	return (a->s6_addr32[2] | htonl(0x02000000)) == htonl(0x02005EFE);
 }
 
+/* Convert a string to an ipv6 address */
+int string_to_ip6(const char *s, struct in6_addr *addr);
+
 #endif /* __NET6_H__ */
diff --git a/lib/net_utils.c b/lib/net_utils.c
index 0fca54d..b0d0364 100644
--- a/lib/net_utils.c
+++ b/lib/net_utils.c
@@ -11,6 +11,8 @@
  */
 
 #include <common.h>
+#include <net6.h>
+#include <linux/ctype.h>
 
 struct in_addr string_to_ip(const char *s)
 {
@@ -43,3 +45,120 @@ struct in_addr string_to_ip(const char *s)
 	addr.s_addr = htonl(addr.s_addr);
 	return addr;
 }
+
+/**
+ * Parses an struct in6_addr from the given string. IPv6 address parsing is a bit
+ * more complicated than v4 due to the flexible format and some of the special
+ * cases (e.g. v4 mapped).
+ *
+ * Examples of valid strings:
+ *   2001:db8::0:1234:1
+ *   2001:0db8:0000:0000:0000:0000:1234:0001
+ *   ::1
+ *   ::ffff:192.168.1.1
+ *
+ * Examples of invalid strings
+ *   2001:db8::0::0          (:: can only appear once)
+ *   2001:db8:192.168.1.1::1 (v4 part can only appear at the end)
+ *   192.168.1.1             (we don't implicity map v4)
+ */
+int string_to_ip6(const char *strpt, struct in6_addr *addrpt)
+{
+	int colon_count = 0;
+	int found_double_colon = 0;
+	int xstart = 0;		/* first zero (double colon) */
+	int len = 7;		/* num words the double colon represents */
+	int i;
+	const char *s = strpt;
+	struct in_addr zero_ip = {.s_addr = 0};
+
+	if (strpt == NULL)
+		return -1;
+
+	/* First pass, verify the syntax and locate the double colon */
+	for (;;) {
+		while (isxdigit((int)*s))
+			s++;
+		if (*s == '\0')
+			break;
+		if (*s != ':') {
+			if (*s == '.' && len >= 2) {
+				struct in_addr v4;
+				while (s != strpt && *(s - 1) != ':')
+					--s;
+				v4 = string_to_ip(s);
+				if (memcmp(&zero_ip, &v4,
+					   sizeof(struct in_addr) != 0)) {
+					len -= 2;
+					break;
+				}
+			}
+			/* This could be a valid address */
+			break;
+		}
+		if (s == strpt) {
+			/* The address begins with a colon */
+			if (*++s != ':')
+				/* Must start with a double colon or a number */
+				goto out_err;
+		} else {
+			s++;
+			if (found_double_colon)
+				len--;
+			else
+				xstart++;
+		}
+
+		if (*s == ':') {
+			if (found_double_colon)
+				/* Two double colons are not allowed */
+				goto out_err;
+			found_double_colon = 1;
+			len -= xstart;
+			s++;
+		}
+
+		if (++colon_count == 7)
+			/* Found all colons */
+			break;
+	}
+
+	if (colon_count == 0 || colon_count > 7)
+		goto out_err;
+	if (*--s == ':')
+		len++;
+
+	/* Second pass, read the address */
+	s = strpt;
+	for (i = 0; i < 8; i++) {
+		int val = 0;
+		char *end;
+
+		if (found_double_colon && i >= xstart && i < xstart + len) {
+			addrpt->s6_addr16[i] = 0;
+			continue;
+		}
+		while (*s == ':')
+			s++;
+
+		if (i == 6 && isdigit((int)*s)) {
+			struct in_addr v4 = string_to_ip(s);
+			if (memcmp(&zero_ip, &v4,
+				   sizeof(struct in_addr)) != 0) {
+				/* Ending with :IPv4-address */
+				addrpt->s6_addr32[3] = v4.s_addr;
+				break;
+			}
+		}
+
+		val = simple_strtoul(s, &end, 16);
+		if (*end != '\0' && *end != ':')
+			goto out_err;
+		addrpt->s6_addr16[i] = htons(val);
+		s = end;
+	}
+	return 0;
+
+out_err:
+	return -1;
+}
-- 
2.5.3

^ permalink raw reply related	[flat|nested] 31+ messages in thread

* [U-Boot] [RFC PATCH 5/8] net: ipv6 support
  2015-10-12  7:43 [U-Boot] [RFC PATCH 0/8] IPv6 support Chris Packham
                   ` (3 preceding siblings ...)
  2015-10-12  7:43 ` [U-Boot] [RFC PATCH 4/8] lib: net_utils: add string_to_ip6 Chris Packham
@ 2015-10-12  7:43 ` Chris Packham
  2015-11-02 20:43   ` Joe Hershberger
  2015-10-12  7:43 ` [U-Boot] [RFC PATCH 6/8] net: TFTP over IPv6 Chris Packham
                   ` (4 subsequent siblings)
  9 siblings, 1 reply; 31+ messages in thread
From: Chris Packham @ 2015-10-12  7:43 UTC (permalink / raw)
  To: u-boot

Adds basic support for IPv6. Neighbor discovery and ping6 are the only
things supported at the moment.

Helped-by: Hanna Hawa <hannah@marvell.com> [endian & alignment fixes]
Signed-off-by: Chris Packham <judge.packham@gmail.com>
---
Now we have something functional. With this you can do something like
'setenv ipaddr6 3ffe::2' and 'ping6 3ffe::1' should work.

I seem to have a problem that when you send a ping6 for a non-existent
address that ends up stuck and the next non-ipv6 net operation tries to
resolve it. I suspect this is because the pending neighbor discovery
information isn't cleaned up properly, I need to look into that.

The environment variable prefixlength6 is a bit fiddly. No-one uses a
netmask6 (it'd mean a lot of ffff:ffff:...) it's almost always done by
including the prefix length in the address (e.g. ip addr add
2001:db8::1/64) I'm contemplating adopting that syntax and dropping
prefixlength6.

This patch is bigger than I'd like it to be but I'm not sure how to
split it up and keep the parts build able.

 common/Kconfig         |  15 ++
 common/cmd_net.c       |  28 ++++
 include/env_callback.h |   9 ++
 include/env_flags.h    |  10 ++
 include/net.h          |   5 +-
 include/net6.h         | 212 ++++++++++++++++++++++++++++
 net/Kconfig            |   5 +
 net/Makefile           |   3 +
 net/ndisc.c            | 269 +++++++++++++++++++++++++++++++++++
 net/ndisc.h            |  27 ++++
 net/net.c              |  36 ++++-
 net/net6.c             | 375 +++++++++++++++++++++++++++++++++++++++++++++++++
 net/ping6.c            | 111 +++++++++++++++
 13 files changed, 1102 insertions(+), 3 deletions(-)
 create mode 100644 net/ndisc.c
 create mode 100644 net/ndisc.h
 create mode 100644 net/net6.c
 create mode 100644 net/ping6.c

diff --git a/common/Kconfig b/common/Kconfig
index 2c42b8e..c72563d 100644
--- a/common/Kconfig
+++ b/common/Kconfig
@@ -389,6 +389,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
@@ -420,6 +429,12 @@ config CMD_PING
 	help
 	  Send ICMP ECHO_REQUEST to network host
 
+config CMD_PING6
+	bool "ping6"
+	depends on CMD_NET6
+	help
+	  Send ICMPv6 ECHO_REQUEST to network host
+
 config CMD_CDP
 	bool "cdp"
 	help
diff --git a/common/cmd_net.c b/common/cmd_net.c
index b2f3c7b..271f91d 100644
--- a/common/cmd_net.c
+++ b/common/cmd_net.c
@@ -11,6 +11,7 @@
 #include <common.h>
 #include <command.h>
 #include <net.h>
+#include <net6.h>
 
 static int netboot_common(enum proto_t, cmd_tbl_t *, int, char * const []);
 
@@ -284,6 +285,33 @@ U_BOOT_CMD(
 );
 #endif
 
+#ifdef CONFIG_CMD_PING6
+int do_ping6(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])
+{
+	if (argc < 2)
+		return -1;
+
+	if (string_to_ip6(argv[1], &net_ping_ip6) != 0)
+		return CMD_RET_USAGE;
+
+	if (net_loop(PING6) < 0) {
+		printf("ping6 failed; host %pI6c is not alive\n",
+		       &net_ping_ip6);
+		return 1;
+	}
+
+	printf("host %pI6c is alive\n", &net_ping_ip6);
+
+	return 0;
+}
+
+U_BOOT_CMD(
+	ping6,  2,      1,      do_ping6,
+	"send ICMPv6 ECHO_REQUEST to network host",
+	"pingAddress"
+);
+#endif /* CONFIG_CMD_PING6 */
+
 #if defined(CONFIG_CMD_CDP)
 
 static void cdp_update_env(void)
diff --git a/include/env_callback.h b/include/env_callback.h
index 90b95b5..9027f3f 100644
--- a/include/env_callback.h
+++ b/include/env_callback.h
@@ -60,6 +60,14 @@
 #define NET_CALLBACKS
 #endif
 
+#ifdef CONFIG_NET6
+#define NET6_CALLBACKS \
+	"ip6addr:ip6addr," \
+	"serverip6:serverip6," \
+	"prefixlength6:prefixlength6,"
+#else
+#define NET6_CALLBACKS
+#endif
 /*
  * This list of callback bindings is static, but may be overridden by defining
  * a new association in the ".callbacks" environment variable.
@@ -68,6 +76,7 @@
 	ENV_DOT_ESCAPE ENV_FLAGS_VAR ":flags," \
 	"baudrate:baudrate," \
 	NET_CALLBACKS \
+	NET6_CALLBACKS \
 	"loadaddr:loadaddr," \
 	SILENT_CALLBACK \
 	SPLASHIMAGE_CALLBACK \
diff --git a/include/env_flags.h b/include/env_flags.h
index 8823fb9..6e1891d 100644
--- a/include/env_flags.h
+++ b/include/env_flags.h
@@ -65,6 +65,15 @@ enum env_flags_varaccess {
 #define NET_FLAGS
 #endif
 
+#ifdef CONFIG_CMD_NET6
+#define NET6_FLAGS \
+	"ip6addr:s," \
+	"serverip6:s," \
+	"prefixlength6:d,"
+#else
+#define NET6_FLAGS
+#endif
+
 #ifndef CONFIG_ENV_OVERWRITE
 #define SERIAL_FLAGS "serial#:so,"
 #else
@@ -74,6 +83,7 @@ enum env_flags_varaccess {
 #define ENV_FLAGS_LIST_STATIC \
 	ETHADDR_FLAGS \
 	NET_FLAGS \
+	NET6_FLAGS \
 	SERIAL_FLAGS \
 	CONFIG_ENV_FLAGS_LIST_STATIC
 
diff --git a/include/net.h b/include/net.h
index 3a787cc..4f59609 100644
--- a/include/net.h
+++ b/include/net.h
@@ -316,6 +316,7 @@ struct vlan_ethernet_hdr {
 #define VLAN_ETHER_HDR_SIZE	(sizeof(struct vlan_ethernet_hdr))
 
 #define PROT_IP		0x0800		/* IP protocol			*/
+#define PROT_IP6        0x86DD          /* IPv6 protocol		*/
 #define PROT_ARP	0x0806		/* IP ARP protocol		*/
 #define PROT_RARP	0x8035		/* IP ARP protocol		*/
 #define PROT_VLAN	0x8100		/* IEEE 802.1q protocol		*/
@@ -512,8 +513,8 @@ extern ushort		net_native_vlan;	/* Our Native VLAN */
 extern int		net_restart_wrap;	/* Tried all network devices */
 
 enum proto_t {
-	BOOTP, RARP, ARP, TFTPGET, DHCP, PING, DNS, NFS, CDP, NETCONS, SNTP,
-	TFTPSRV, TFTPPUT, LINKLOCAL
+	BOOTP, RARP, ARP, TFTPGET, DHCP, PING, PING6, DNS, NFS, CDP, NETCONS,
+	SNTP, TFTPSRV, TFTPPUT, TFTP6, LINKLOCAL
 };
 
 extern char	net_boot_file_name[128];/* Boot File name */
diff --git a/include/net6.h b/include/net6.h
index a41eb87..a0374df 100644
--- a/include/net6.h
+++ b/include/net6.h
@@ -22,6 +22,16 @@ struct in6_addr {
 #define s6_addr32	in6_u.u6_addr32
 };
 
+#define IN6ADDRSZ	sizeof(struct in6_addr)
+#define INETHADDRSZ	sizeof(net_ethaddr)
+
+#define IPV6_ADDRSCOPE_INTF	0x01
+#define IPV6_ADDRSCOPE_LINK	0x02
+#define IPV6_ADDRSCOPE_AMDIN	0x04
+#define IPV6_ADDRSCOPE_SITE	0x05
+#define IPV6_ADDRSCOPE_ORG	0x08
+#define IPV6_ADDRSCOPE_GLOBAL	0x0E
+
 /**
  * struct ipv6hdr - Internet Protocol V6 (IPv6) header.
  *
@@ -45,6 +55,154 @@ struct ip6_hdr {
 	struct in6_addr	daddr;
 };
 
+#define IP6_HDR_SIZE (sizeof(struct ip6_hdr))
+
+/* Handy for static initialisations of struct in6_addr, atlhough the
+ * c99 '= { 0 }' idiom might work depending on you compiler. */
+#define ZERO_IPV6_ADDR { { { 0x00, 0x00, 0x00, 0x00, \
+			  0x00, 0x00, 0x00, 0x00, \
+			  0x00, 0x00, 0x00, 0x00, \
+			  0x00, 0x00, 0x00, 0x00 } } }
+
+#define IPV6_LINK_LOCAL_PREFIX	0xfe80
+
+struct udp_hdr {
+	__be16		udp_src;	/* UDP source port		*/
+	__be16		udp_dst;	/* UDP destination port		*/
+	__be16		udp_len;	/* Length of UDP packet		*/
+	__be16		udp_xsum;	/* Checksum			*/
+};
+
+#define IP6_UDPHDR_SIZE (sizeof(struct udp_hdr))
+
+enum {
+	__ND_OPT_PREFIX_INFO_END	= 0,
+	ND_OPT_SOURCE_LL_ADDR		= 1,
+	ND_OPT_TARGET_LL_ADDR		= 2,
+	ND_OPT_PREFIX_INFO		= 3,
+	ND_OPT_REDIRECT_HDR		= 4,
+	ND_OPT_MTU			= 5,
+	__ND_OPT_MAX
+};
+
+/* ICMPv6 */
+#define IPPROTO_ICMPV6			58
+/* hop limit for neighbour discovery packets */
+#define IPV6_NDISC_HOPLIMIT             255
+#define NDISC_TIMEOUT			5000UL
+#define NDISC_TIMEOUT_COUNT             3
+
+struct icmp6hdr {
+	__u8	icmp6_type;
+#define IPV6_ICMP_ECHO_REQUEST			128
+#define IPV6_ICMP_ECHO_REPLY			129
+#define IPV6_NDISC_ROUTER_SOLICITATION		133
+#define IPV6_NDISC_ROUTER_ADVERTISEMENT		134
+#define IPV6_NDISC_NEIGHBOUR_SOLICITATION	135
+#define IPV6_NDISC_NEIGHBOUR_ADVERTISEMENT	136
+#define IPV6_NDISC_REDIRECT			137
+	__u8	icmp6_code;
+	__be16	icmp6_cksum;
+
+	union {
+		__be32	un_data32[1];
+		__be16	un_data16[2];
+		__u8	un_data8[4];
+
+		struct icmpv6_echo {
+			__be16		identifier;
+			__be16		sequence;
+		} u_echo;
+
+		struct icmpv6_nd_advt {
+#if defined(__LITTLE_ENDIAN_BITFIELD)
+			__be32		reserved:5,
+					override:1,
+					solicited:1,
+					router:1,
+					reserved2:24;
+#elif defined(__BIG_ENDIAN_BITFIELD)
+			__be32		router:1,
+					solicited:1,
+					override:1,
+					reserved:29;
+#else
+#error	"Please fix <asm/byteorder.h>"
+#endif
+		} u_nd_advt;
+
+		struct icmpv6_nd_ra {
+			__u8		hop_limit;
+#if defined(__LITTLE_ENDIAN_BITFIELD)
+			__u8		reserved:6,
+					other:1,
+					managed:1;
+
+#elif defined(__BIG_ENDIAN_BITFIELD)
+			__u8		managed:1,
+					other:1,
+					reserved:6;
+#else
+#error	"Please fix <asm/byteorder.h>"
+#endif
+			__be16		rt_lifetime;
+		} u_nd_ra;
+	} icmp6_dataun;
+#define icmp6_identifier	icmp6_dataun.u_echo.identifier
+#define icmp6_sequence		icmp6_dataun.u_echo.sequence
+#define icmp6_pointer		icmp6_dataun.un_data32[0]
+#define icmp6_mtu		icmp6_dataun.un_data32[0]
+#define icmp6_unused		icmp6_dataun.un_data32[0]
+#define icmp6_maxdelay		icmp6_dataun.un_data16[0]
+#define icmp6_router		icmp6_dataun.u_nd_advt.router
+#define icmp6_solicited		icmp6_dataun.u_nd_advt.solicited
+#define icmp6_override		icmp6_dataun.u_nd_advt.override
+#define icmp6_ndiscreserved	icmp6_dataun.u_nd_advt.reserved
+#define icmp6_hop_limit		icmp6_dataun.u_nd_ra.hop_limit
+#define icmp6_addrconf_managed	icmp6_dataun.u_nd_ra.managed
+#define icmp6_addrconf_other	icmp6_dataun.u_nd_ra.other
+#define icmp6_rt_lifetime	icmp6_dataun.u_nd_ra.rt_lifetime
+};
+
+struct nd_msg {
+	struct icmp6hdr	icmph;
+	struct in6_addr	target;
+	__u8		opt[0];
+};
+
+struct rs_msg {
+	struct icmp6hdr	icmph;
+	__u8		opt[0];
+};
+
+struct ra_msg {
+	struct icmp6hdr	icmph;
+	__u32		reachable_time;
+	__u32		retrans_timer;
+};
+
+struct echo_msg {
+	struct icmp6hdr	icmph;
+	__u16		id;
+	__u16		sequence;
+};
+
+struct nd_opt_hdr {
+	__u8		nd_opt_type;
+	__u8		nd_opt_len;
+} __attribute__((__packed__));
+
+extern struct in6_addr const net_null_addr_ip6;	/* NULL IPv6 address */
+extern struct in6_addr net_gateway6;	/* Our gateways IPv6 address */
+extern struct in6_addr net_ip6;	/* Our IPv6 addr (0 = unknown) */
+extern struct in6_addr net_link_local_ip6;	/* Our link local IPv6 addr */
+extern u_int32_t net_prefix_length;	/* Our prefixlength (0 = unknown) */
+extern struct in6_addr net_server_ip6;	/* Server IPv6 addr (0 = unknown) */
+
+#ifdef CONFIG_CMD_PING
+extern struct in6_addr net_ping_ip6;	/* the ipv6 address to ping */
+#endif
+
 /* ::ffff:0:0/96 is reserved for v4 mapped addresses */
 static inline int ipv6_addr_v4mapped(const struct in6_addr *a)
 {
@@ -61,4 +219,58 @@ static inline int ipv6_addr_is_isatap(const struct in6_addr *a)
 /* Convert a string to an ipv6 address */
 int string_to_ip6(const char *s, struct in6_addr *addr);
 
+/* check that an IPv6 address is unspecified (zero) */
+int ip6_is_unspecified_addr(struct in6_addr *addr);
+
+/* check that an IPv6 address is ours */
+int ip6_is_our_addr(struct in6_addr *addr);
+
+void ip6_make_lladdr(struct in6_addr *lladr, unsigned char const enetaddr[6]);
+
+void ip6_make_SNMA(struct in6_addr *mcast_addr, struct in6_addr *ip6_addr);
+
+void ip6_make_mult_ethdstaddr(unsigned char enetaddr[6],
+			      struct in6_addr *mcast_addr);
+
+/* check if neighbour is in the same subnet as us */
+int ip6_addr_in_subnet(struct in6_addr *our_addr, struct in6_addr *neigh_addr,
+		       __u32 prefix_length);
+
+unsigned int csum_partial(const unsigned char *buff, int len, unsigned int sum);
+
+unsigned short int csum_ipv6_magic(struct in6_addr *saddr,
+				   struct in6_addr *daddr, __u16 len,
+				   unsigned short proto, unsigned int csum);
+
+int ip6_add_hdr(uchar *xip, struct in6_addr *src, struct in6_addr *dest,
+		int nextheader, int hoplimit, int payload_len);
+
+/* send a neighbour discovery solicitation message */
+void ip6_NDISC_Request(void);
+
+/* call back routine when ND timer has gone off */
+void ip6_NDISC_TimeoutCheck(void);
+
+/* initialises the ND data */
+void ip6_NDISC_init(void);
+
+/* sends an IPv6 echo request to a host */
+int ping6_send(void);
+
+/* starts a Ping6 process */
+void ping6_start(void);
+
+/* handles reception of icmpv6 echo request/reply */
+void ping6_receive(struct ethernet_hdr *et, struct ip6_hdr *ip6,
+			  int len);
+
+/* handler for incoming IPv6 echo packet */
+void net_ip6_handler(struct ethernet_hdr *et, struct ip6_hdr *ip6,
+			    int len);
+
+/* copy IPv6 */
+static inline void net_copy_ip6(void *to, void *from)
+{
+	memcpy((void *)to, from, sizeof(struct in6_addr));
+}
 #endif /* __NET6_H__ */
diff --git a/net/Kconfig b/net/Kconfig
index 77a2f7e..ee198c1 100644
--- a/net/Kconfig
+++ b/net/Kconfig
@@ -22,4 +22,9 @@ config NETCONSOLE
 	  Support the 'nc' input/output device for networked console.
 	  See README.NetConsole for details.
 
+config NET6
+	bool "IPv6 support"
+	help
+	  Support for IPv6
+
 endif   # if NET
diff --git a/net/Makefile b/net/Makefile
index e9cc8ad..a85a5c6 100644
--- a/net/Makefile
+++ b/net/Makefile
@@ -20,3 +20,6 @@ obj-$(CONFIG_CMD_PING) += ping.o
 obj-$(CONFIG_CMD_RARP) += rarp.o
 obj-$(CONFIG_CMD_SNTP) += sntp.o
 obj-$(CONFIG_CMD_NET)  += tftp.o
+obj-$(CONFIG_CMD_NET6) += net6.o
+obj-$(CONFIG_CMD_NET6) += ndisc.o
+obj-$(CONFIG_CMD_PING6) += ping6.o
diff --git a/net/ndisc.c b/net/ndisc.c
new file mode 100644
index 0000000..41883d7
--- /dev/null
+++ b/net/ndisc.c
@@ -0,0 +1,269 @@
+/*
+ * net/ndisc.c
+ *
+ * (C) Copyright 2013 Allied Telesis Labs NZ
+ *
+ * SPDX-License-Identifier:	GPL-2.0+
+ */
+#define DEBUG
+#include <common.h>
+#include <net.h>
+#include <net6.h>
+#include "ndisc.h"
+
+/* IPv6 destination address of packet waiting for ND */
+struct in6_addr net_nd_sol_packet_ip6 = ZERO_IPV6_ADDR;
+/* IPv6 address we are expecting ND advert from */
+struct in6_addr net_nd_rep_packet_ip6 = ZERO_IPV6_ADDR;
+/* MAC destination address of packet waiting for ND */
+uchar *net_nd_packet_mac;
+/* pointer to packet waiting to be transmitted after ND is resolved */
+uchar *net_nd_tx_packet;
+uchar net_nd_packet_buf[PKTSIZE_ALIGN + PKTALIGN];
+/* size of packet waiting to be transmitted */
+int net_nd_tx_packet_size;
+/* the timer for ND resolution */
+ulong net_nd_timer_start;
+/* the number of requests we have sent so far */
+int net_nd_try;
+
+#define IP6_NDISC_OPT_SPACE(len) (((len)+2+7)&~7)
+
+/**
+ * Insert an iption into a neighbor discovery packet.
+ * Returns the number of bytes inserted (which may be >= len)
+ */
+static int
+ip6_ndisc_insert_option(struct nd_msg *ndisc, int type, u8 *data, int len)
+{
+	int space = IP6_NDISC_OPT_SPACE(len);
+
+	ndisc->opt[0] = type;
+	ndisc->opt[1] = space >> 3;
+	memcpy(&ndisc->opt[2], data, len);
+	len += 2;
+
+	/* fill the remainder with 0 */
+	if ((space - len) > 0)
+		memset(&ndisc->opt[len], 0, space - len);
+
+	return space;
+}
+
+/**
+ * Extract the Ethernet address from a neighbor discovery packet.
+ * Note that the link layer address could be anything but the only networking
+ * media that u-boot supports is Ethernet so we assume we're extracting a 6
+ * byte Ethernet MAC address.
+ */
+static void ip6_ndisc_extract_enetaddr(struct nd_msg *ndisc, uchar enetaddr[6])
+{
+	memcpy(enetaddr, &ndisc->opt[2], 6);
+}
+
+/**
+ * Check to see if the neighbor discovery packet has
+ * the specified option set.
+ */
+static int ip6_ndisc_has_option(struct ip6_hdr *ip6, __u8 type)
+{
+	struct nd_msg *ndisc = (struct nd_msg *)(((uchar *)ip6) + IP6_HDR_SIZE);
+
+	if (ip6->payload_len <= sizeof(struct icmp6hdr))
+		return 0;
+
+	return ndisc->opt[0] == type;
+}
+
+static void ip6_send_ns(struct in6_addr *neigh_addr)
+{
+	struct in6_addr dst_adr;
+	unsigned char enetaddr[6];
+	struct nd_msg *msg;
+	__u16 len;
+	uchar *pkt;
+
+	debug("sending neighbor solicitation for %pI6c our address %pI6c\n",
+	      neigh_addr, &net_link_local_ip6);
+
+	/* calculate src, dest IPv6 addr and dest Eth addr */
+	ip6_make_SNMA(&dst_adr, neigh_addr);
+	ip6_make_mult_ethdstaddr(enetaddr, &dst_adr);
+	len = sizeof(struct icmp6hdr) + IN6ADDRSZ +
+	    IP6_NDISC_OPT_SPACE(INETHADDRSZ);
+
+	pkt = (uchar *)net_tx_packet;
+	pkt += net_set_ether(pkt, enetaddr, PROT_IP6);
+	pkt += ip6_add_hdr(pkt, &net_link_local_ip6, &dst_adr, IPPROTO_ICMPV6,
+			   IPV6_NDISC_HOPLIMIT, len);
+
+	/* ICMPv6 - NS */
+	msg = (struct nd_msg *)pkt;
+	msg->icmph.icmp6_type = IPV6_NDISC_NEIGHBOUR_SOLICITATION;
+	msg->icmph.icmp6_code = 0;
+	memset(&msg->icmph.icmp6_cksum, 0, sizeof(__be16));
+	memset(&msg->icmph.icmp6_unused, 0, sizeof(__be32));
+
+	/* Set the target address and llsaddr option */
+	net_copy_ip6(&msg->target, neigh_addr);
+	ip6_ndisc_insert_option(msg, ND_OPT_SOURCE_LL_ADDR, net_ethaddr,
+				INETHADDRSZ);
+
+	/* checksum */
+	msg->icmph.icmp6_cksum = csum_ipv6_magic(&net_link_local_ip6, &dst_adr,
+						 len, IPPROTO_ICMPV6,
+						 csum_partial((__u8 *)msg, len, 0));
+
+	pkt += len;
+
+	/* send it! */
+	net_send_packet(net_tx_packet, (pkt - net_tx_packet));
+}
+
+static void
+ip6_send_na(uchar *eth_dst_addr, struct in6_addr *neigh_addr,
+	    struct in6_addr *target)
+{
+	struct nd_msg *msg;
+	__u16 len;
+	uchar *pkt;
+
+	debug("sending neighbor advertisement for %pI6c to %pI6c (%pM)\n",
+	      target, neigh_addr, eth_dst_addr);
+
+	len = sizeof(struct icmp6hdr) + IN6ADDRSZ +
+	    IP6_NDISC_OPT_SPACE(INETHADDRSZ);
+
+	pkt = (uchar *)net_tx_packet;
+	pkt += net_set_ether(pkt, eth_dst_addr, PROT_IP6);
+	pkt += ip6_add_hdr(pkt, &net_link_local_ip6, neigh_addr,
+			   IPPROTO_ICMPV6, IPV6_NDISC_HOPLIMIT, len);
+
+	/* ICMPv6 - NS */
+	msg = (struct nd_msg *)pkt;
+	msg->icmph.icmp6_type = IPV6_NDISC_NEIGHBOUR_ADVERTISEMENT;
+	msg->icmph.icmp6_code = 0;
+	memset(&msg->icmph.icmp6_cksum, 0, sizeof(__be16));
+	memset(&msg->icmph.icmp6_unused, 0, sizeof(__be32));
+
+	/* Set the target address and lltargetaddr option */
+	net_copy_ip6(&msg->target, target);
+	ip6_ndisc_insert_option(msg, ND_OPT_TARGET_LL_ADDR, net_ethaddr,
+				INETHADDRSZ);
+
+	/* checksum */
+	msg->icmph.icmp6_cksum = csum_ipv6_magic(&net_link_local_ip6,
+						 neigh_addr, len, IPPROTO_ICMPV6,
+						 csum_partial((__u8 *)msg, len, 0));
+
+	pkt += len;
+
+	/* send it! */
+	net_send_packet(net_tx_packet, (pkt - net_tx_packet));
+}
+
+void ip6_NDISC_Request(void)
+{
+	if (!ip6_addr_in_subnet(&net_ip6, &net_nd_sol_packet_ip6,
+				net_prefix_length)) {
+		if (ip6_is_unspecified_addr(&net_gateway6)) {
+			puts("## Warning: gatewayip6 is needed but not set\n");
+			net_nd_rep_packet_ip6 = net_nd_sol_packet_ip6;
+		} else {
+			net_nd_rep_packet_ip6 = net_gateway6;
+		}
+	} else {
+		net_nd_rep_packet_ip6 = net_nd_sol_packet_ip6;
+	}
+
+	ip6_send_ns(&net_nd_rep_packet_ip6);
+}
+
+void ip6_NDISC_TimeoutCheck(void)
+{
+	ulong t;
+
+	if (ip6_is_unspecified_addr(&net_nd_sol_packet_ip6))
+		return;
+
+	t = get_timer(0);
+
+	/* check for NDISC timeout */
+	if ((t - net_nd_timer_start) > NDISC_TIMEOUT) {
+		net_nd_try++;
+		if (net_nd_try >= NDISC_TIMEOUT_COUNT) {
+			puts("\nNeighbour discovery retry count exceeded; "
+			     "starting again\n");
+			net_nd_try = 0;
+			net_start_again();
+		} else {
+			net_nd_timer_start = t;
+			ip6_NDISC_Request();
+		}
+	}
+}
+
+void ip6_NDISC_init(void)
+{
+	net_nd_packet_mac = NULL;
+	net_nd_tx_packet = NULL;
+	net_nd_sol_packet_ip6 = net_null_addr_ip6;
+	net_nd_rep_packet_ip6 = net_null_addr_ip6;
+	net_nd_tx_packet = NULL;
+
+	if (!net_nd_tx_packet) {
+		net_nd_tx_packet = &net_nd_packet_buf[0] + (PKTALIGN - 1);
+		net_nd_tx_packet -= (ulong)net_nd_tx_packet % PKTALIGN;
+		net_nd_tx_packet_size = 0;
+	}
+}
+
+void ndisc_receive(struct ethernet_hdr *et, struct ip6_hdr *ip6, int len)
+{
+	struct icmp6hdr *icmp =
+	    (struct icmp6hdr *)(((uchar *)ip6) + IP6_HDR_SIZE);
+	struct nd_msg *ndisc = (struct nd_msg *)icmp;
+	uchar neigh_eth_addr[6];
+
+	switch (icmp->icmp6_type) {
+	case IPV6_NDISC_NEIGHBOUR_SOLICITATION:
+		debug("received neighbor solicitation for %pI6c from %pI6c\n",
+		      &ndisc->target, &ip6->saddr);
+		if (ip6_is_our_addr(&ndisc->target) &&
+		    ip6_ndisc_has_option(ip6, ND_OPT_SOURCE_LL_ADDR)) {
+			ip6_ndisc_extract_enetaddr(ndisc, neigh_eth_addr);
+			ip6_send_na(neigh_eth_addr, &ip6->saddr,
+				    &ndisc->target);
+		}
+		break;
+
+	case IPV6_NDISC_NEIGHBOUR_ADVERTISEMENT:
+		/* are we waiting for a reply ? */
+		if (ip6_is_unspecified_addr(&net_nd_sol_packet_ip6))
+			break;
+
+		if ((memcmp(&ndisc->target, &net_nd_rep_packet_ip6,
+			    sizeof(struct in6_addr)) == 0) &&
+		    ip6_ndisc_has_option(ip6, ND_OPT_TARGET_LL_ADDR)) {
+			ip6_ndisc_extract_enetaddr(ndisc, neigh_eth_addr);
+
+			/* save address for later use */
+			if (net_nd_packet_mac != NULL)
+				memcpy(net_nd_packet_mac, neigh_eth_addr, 6);
+
+			/* modify header, and transmit it */
+			memcpy(((struct ethernet_hdr *)net_nd_tx_packet)->et_dest,
+			       neigh_eth_addr, 6);
+			net_send_packet(net_nd_tx_packet,
+					net_nd_tx_packet_size);
+
+			/* no ND request pending now */
+			net_nd_sol_packet_ip6 = net_null_addr_ip6;
+			net_nd_tx_packet_size = 0;
+			net_nd_packet_mac = NULL;
+		}
+		break;
+	default:
+		debug("Unexpected ICMPv6 type 0x%x\n", icmp->icmp6_type);
+	}
+}
diff --git a/net/ndisc.h b/net/ndisc.h
new file mode 100644
index 0000000..7ade0fc
--- /dev/null
+++ b/net/ndisc.h
@@ -0,0 +1,27 @@
+/*
+ * net/ndisc.h
+ *
+ * (C) Copyright 2013 Allied Telesis Labs NZ
+ *
+ * SPDX-License-Identifier:	GPL-2.0+
+ */
+
+/* IPv6 destination address of packet waiting for ND */
+extern struct in6_addr net_nd_sol_packet_ip6;
+/* IPv6 address we are expecting ND advert from */
+extern struct in6_addr net_nd_rep_packet_ip6;
+/* MAC destination address of packet waiting for ND */
+extern uchar *net_nd_packet_mac;
+/* pointer to packet waiting to be transmitted after ND is resolved */
+extern uchar *net_nd_tx_packet;
+extern uchar net_nd_packet_buf[PKTSIZE_ALIGN + PKTALIGN];
+/* size of packet waiting to be transmitted */
+extern int net_nd_tx_packet_size;
+/* the timer for ND resolution */
+extern ulong net_nd_timer_start;
+/* the number of requests we have sent so far */
+extern int net_nd_try;
+
+void ndisc_receive(struct ethernet_hdr *et, struct ip6_hdr *ip6, int len);
+void ip6_NDISC_Request(void);
+void ip6_NDISC_TimeoutCheck(void);
diff --git a/net/net.c b/net/net.c
index a115ce2..349a18e 100644
--- a/net/net.c
+++ b/net/net.c
@@ -86,6 +86,7 @@
 #include <environment.h>
 #include <errno.h>
 #include <net.h>
+#include <net6.h>
 #include <net/tftp.h>
 #if defined(CONFIG_STATUS_LED)
 #include <miiphy.h>
@@ -94,6 +95,7 @@
 #include <watchdog.h>
 #include <linux/compiler.h>
 #include "arp.h"
+#include "ndisc.h"
 #include "bootp.h"
 #include "cdp.h"
 #if defined(CONFIG_CMD_DNS)
@@ -311,6 +313,7 @@ static int on_dnsip(const char *name, const char *value, enum env_op op,
 U_BOOT_ENV_CALLBACK(dnsip, on_dnsip);
 #endif
 
+
 /*
  * Check if autoload is enabled. If so, use either NFS or TFTP to download
  * the boot file.
@@ -341,8 +344,12 @@ void net_auto_load(void)
 
 static void net_init_loop(void)
 {
-	if (eth_get_dev())
+	if (eth_get_dev()) {
 		memcpy(net_ethaddr, eth_get_ethaddr(), 6);
+#ifdef CONFIG_CMD_NET6
+		ip6_make_lladdr(&net_link_local_ip6, net_ethaddr);
+#endif
+	}
 
 	return;
 }
@@ -376,6 +383,9 @@ void net_init(void)
 				(i + 1) * PKTSIZE_ALIGN;
 		}
 		arp_init();
+#ifdef CONFIG_CMD_NET6
+		ip6_NDISC_init();
+#endif
 		net_clear_handlers();
 
 		/* Only need to setup buffer pointers once. */
@@ -478,6 +488,11 @@ restart:
 			ping_start();
 			break;
 #endif
+#ifdef CONFIG_CMD_PING6
+		case PING6:
+			ping6_start();
+			break;
+#endif
 #if defined(CONFIG_CMD_NFS)
 		case NFS:
 			nfs_start();
@@ -555,6 +570,9 @@ restart:
 		if (ctrlc()) {
 			/* cancel any ARP that may not have completed */
 			net_arp_wait_packet_ip.s_addr = 0;
+#ifdef CONFIG_CMD_NET6
+			net_nd_sol_packet_ip6 = net_null_addr_ip6;
+#endif
 
 			net_cleanup_loop();
 			eth_halt();
@@ -570,6 +588,9 @@ restart:
 		}
 
 		arp_timeout_check();
+#ifdef CONFIG_CMD_NET6
+		ip6_NDISC_TimeoutCheck();
+#endif
 
 		/*
 		 *	Check for a timeout, and run the timeout handler
@@ -1141,6 +1162,11 @@ void net_process_received_packet(uchar *in_packet, int len)
 		rarp_receive(ip, len);
 		break;
 #endif
+#ifdef CONFIG_CMD_NET6
+	case PROT_IP6:
+		net_ip6_handler(et, (struct ip6_hdr *)ip, len);
+		break;
+#endif
 	case PROT_IP:
 		debug_cond(DEBUG_NET_PKT, "Got IP\n");
 		/* Before we start poking the header, make sure it is there */
@@ -1295,6 +1321,14 @@ static int net_check_prereq(enum proto_t protocol)
 		}
 		goto common;
 #endif
+#ifdef CONFIG_CMD_PING6
+	case PING6:
+		if (ip6_is_unspecified_addr(&net_ping_ip6)) {
+			puts("*** ERROR: ping address not given\n");
+			return 1;
+		}
+		goto common;
+#endif
 #if defined(CONFIG_CMD_SNTP)
 	case SNTP:
 		if (net_ntp_server.s_addr == 0) {
diff --git a/net/net6.c b/net/net6.c
new file mode 100644
index 0000000..2315704
--- /dev/null
+++ b/net/net6.c
@@ -0,0 +1,375 @@
+/*
+ * Simple IPv6 network layer implementation.
+ *
+ * Based and/or adapted from the IPv4 network layer in net.[hc]
+ *
+ * (C) Copyright 2013 Allied Telesis Labs NZ
+ *
+ * SPDX-License-Identifier:	GPL-2.0+
+ */
+
+/*
+ * General Desription:
+ *
+ * The user interface supports commands for TFTP6.
+ * Also, we support Neighbour discovery internally. Depending on available
+ * data, these interact as follows:
+ *
+ * Neighbour Discovery:
+ *
+ *      Prerequisites:  - own ethernet address
+ *                      - own IPv6 address
+ *                      - TFTP server IPv6 address
+ *      We want:        - TFTP server ethernet address
+ *      Next step:      TFTP
+ *
+ * TFTP over IPv6:
+ *
+ *      Prerequisites:  - own ethernet address
+ *                      - own IPv6 address
+ *                      - TFTP server IPv6 address
+ *                      - TFTP server ethernet address
+ *                      - name of bootfile (if unknown, we use a default name
+ *                        derived from our own IPv6 address)
+ *      We want:        - load the boot file
+ *      Next step:      none
+ *
+ */
+#define DEBUG
+#include <common.h>
+#include <environment.h>
+#include <net.h>
+#include <net6.h>
+#include "ndisc.h"
+
+/* NULL IPv6 address */
+struct in6_addr const net_null_addr_ip6 = ZERO_IPV6_ADDR;
+/* Our gateway's IPv6 address */
+struct in6_addr net_gateway6 = ZERO_IPV6_ADDR;
+/* Our IPv6 addr (0 = unknown) */
+struct in6_addr net_ip6 = ZERO_IPV6_ADDR;
+/* Our link local IPv6 addr (0 = unknown) */
+struct in6_addr net_link_local_ip6 = ZERO_IPV6_ADDR;
+/* set server IPv6 addr (0 = unknown) */
+struct in6_addr net_server_ip6 = ZERO_IPV6_ADDR;
+/* The prefix length of our network */
+u_int32_t net_prefix_length;
+
+static int on_ip6addr(const char *name, const char *value, enum env_op op,
+		      int flags)
+{
+	if (flags & H_PROGRAMMATIC)
+		return 0;
+
+	return string_to_ip6(value, &net_ip6);
+}
+
+U_BOOT_ENV_CALLBACK(ip6addr, on_ip6addr);
+
+static int on_gatewayip6(const char *name, const char *value, enum env_op op,
+			 int flags)
+{
+	if (flags & H_PROGRAMMATIC)
+		return 0;
+
+	return string_to_ip6(value, &net_gateway6);
+}
+
+U_BOOT_ENV_CALLBACK(gatewayip6, on_gatewayip6);
+
+static int on_prefixlength6(const char *name, const char *value, enum env_op op,
+			    int flags)
+{
+	if (flags & H_PROGRAMMATIC)
+		return 0;
+
+	net_prefix_length = simple_strtoul(value, NULL, 10);
+
+	return 0;
+}
+
+U_BOOT_ENV_CALLBACK(prefixlength6, on_prefixlength6);
+
+static int on_serverip6(const char *name, const char *value, enum env_op op,
+			int flags)
+{
+	if (flags & H_PROGRAMMATIC)
+		return 0;
+
+	return string_to_ip6(value, &net_server_ip6);
+}
+
+U_BOOT_ENV_CALLBACK(serverip6, on_serverip6);
+
+int ip6_is_unspecified_addr(struct in6_addr *addr)
+{
+	return (addr->s6_addr32[0] | addr->s6_addr32[1] |
+		addr->s6_addr32[2] | addr->s6_addr32[3]) == 0;
+}
+
+/**
+ * We have 2 addresses that we should respond to. A link
+ * local address and a global address. This returns true
+ * if the specified address matches either of these.
+ */
+int ip6_is_our_addr(struct in6_addr *addr)
+{
+	return memcmp(addr, &net_link_local_ip6, sizeof(struct in6_addr)) == 0 ||
+	       memcmp(addr, &net_ip6, sizeof(struct in6_addr)) == 0;
+}
+
+void ip6_make_eui(unsigned char eui[8], unsigned char const enetaddr[6])
+{
+	memcpy(eui, enetaddr, 3);
+	memcpy(&eui[5], &enetaddr[3], 3);
+	eui[3] = 0xFF;
+	eui[4] = 0xFE;
+	eui[0] ^= 2;		/* "u" bit set to indicate global scope */
+}
+
+void ip6_make_lladdr(struct in6_addr *lladr, unsigned char const enetaddr[6])
+{
+	uchar eui[8];
+
+	memset(lladr, 0, sizeof(struct in6_addr));
+	lladr->s6_addr16[0] = htons(IPV6_LINK_LOCAL_PREFIX);
+	ip6_make_eui(eui, enetaddr);
+	memcpy(&lladr->s6_addr[8], eui, 8);
+}
+
+/*
+ * Given an IPv6 address generate an equivalent Solicited Node Multicast
+ * Address (SNMA) as described in RFC2461.
+ */
+void ip6_make_SNMA(struct in6_addr *mcast_addr, struct in6_addr *ip6_addr)
+{
+	memset(mcast_addr, 0, sizeof(struct in6_addr));
+	mcast_addr->s6_addr[0] = 0xff;
+	mcast_addr->s6_addr[1] = IPV6_ADDRSCOPE_LINK;
+	mcast_addr->s6_addr[11] = 0x01;
+	mcast_addr->s6_addr[12] = 0xff;
+	mcast_addr->s6_addr[13] = ip6_addr->s6_addr[13];
+	mcast_addr->s6_addr[14] = ip6_addr->s6_addr[14];
+	mcast_addr->s6_addr[15] = ip6_addr->s6_addr[15];
+}
+
+/*
+ * Given an IPv6 address generate the multicast MAC address that corresponds to
+ * it.
+ */
+void
+ip6_make_mult_ethdstaddr(unsigned char enetaddr[6], struct in6_addr *mcast_addr)
+{
+	enetaddr[0] = 0x33;
+	enetaddr[1] = 0x33;
+	memcpy(&enetaddr[2], &mcast_addr->s6_addr[12], 4);
+}
+
+int
+ip6_addr_in_subnet(struct in6_addr *our_addr, struct in6_addr *neigh_addr,
+		   __u32 plen)
+{
+	__be32 *addr_dwords;
+	__be32 *neigh_dwords;
+
+	addr_dwords = our_addr->s6_addr32;
+	neigh_dwords = neigh_addr->s6_addr32;
+
+	while (plen > 32) {
+		if (*addr_dwords++ != *neigh_dwords++)
+			return 0;
+
+		plen -= 32;
+	}
+
+	/* Check any remaining bits. */
+	if (plen > 0) {
+		/* parameters are in network byte order.
+		   Does this work on a LE host? */
+		if ((*addr_dwords >> (32 - plen)) !=
+		    (*neigh_dwords >> (32 - plen))) {
+			return 0;
+		}
+	}
+
+	return 1;
+}
+
+static inline unsigned int csum_fold(unsigned int sum)
+{
+	sum = (sum & 0xffff) + (sum >> 16);
+	sum = (sum & 0xffff) + (sum >> 16);
+
+	return ~sum;
+}
+
+static __u32 csum_do_csum(const __u8 *buff, int len)
+{
+	int odd, count;
+	unsigned long result = 0;
+
+	if (len <= 0)
+		goto out;
+	odd = 1 & (unsigned long)buff;
+	if (odd) {
+		result = *buff;
+		len--;
+		buff++;
+	}
+	count = len >> 1;	/* nr of 16-bit words.. */
+	if (count) {
+		if (2 & (unsigned long)buff) {
+			result += *(unsigned short *)buff;
+			count--;
+			len -= 2;
+			buff += 2;
+		}
+		count >>= 1;	/* nr of 32-bit words.. */
+		if (count) {
+			unsigned long carry = 0;
+			do {
+				unsigned long w = *(unsigned long *)buff;
+				count--;
+				buff += 4;
+				result += carry;
+				result += w;
+				carry = (w > result);
+			} while (count);
+			result += carry;
+			result = (result & 0xffff) + (result >> 16);
+		}
+		if (len & 2) {
+			result += *(unsigned short *)buff;
+			buff += 2;
+		}
+	}
+	if (len & 1)
+		result += (*buff << 8);
+	result = ~csum_fold(result);
+	if (odd)
+		result = ((result >> 8) & 0xff) | ((result & 0xff) << 8);
+out:
+	return result;
+}
+
+unsigned int csum_partial(const unsigned char *buff, int len, unsigned int sum)
+{
+	unsigned int result = csum_do_csum(buff, len);
+
+	/* add in old sum, and carry.. */
+	result += sum;
+	/* 16+c bits -> 16 bits */
+	result = (result & 0xffff) + (result >> 16);
+	return result;
+}
+
+unsigned short int
+csum_ipv6_magic(struct in6_addr *saddr, struct in6_addr *daddr,
+		__u16 len, unsigned short proto, unsigned int csum)
+{
+	int i;
+	int carry;
+	__u32 ulen;
+	__u32 uproto;
+	unsigned int finalsum;
+
+	for (i = 0; i < 4; i++) {
+		csum += saddr->s6_addr32[i];
+		carry = (csum < saddr->s6_addr32[i]);
+		csum += carry;
+
+		csum += daddr->s6_addr32[i];
+		carry = (csum < daddr->s6_addr32[i]);
+		csum += carry;
+	}
+
+	ulen = htonl((__u32)len);
+	csum += ulen;
+	carry = (csum < ulen);
+	csum += carry;
+
+	uproto = htonl(proto);
+	csum += uproto;
+	carry = (csum < uproto);
+	csum += carry;
+
+	finalsum = csum_fold(csum);
+	if ((finalsum & 0xffff) == 0x0000)
+		return 0xffff;
+	else if ((finalsum & 0xffff) == 0xffff)
+		return 0x0000;
+	else
+		return finalsum;
+}
+
+int
+ip6_add_hdr(uchar *xip, struct in6_addr *src, struct in6_addr *dest,
+	    int nextheader, int hoplimit, int payload_len)
+{
+	struct ip6_hdr *ip6 = (struct ip6_hdr *)xip;
+
+	ip6->version = 6;
+	ip6->priority = 0;
+	ip6->flow_lbl[0] = 0;
+	ip6->flow_lbl[1] = 0;
+	ip6->flow_lbl[2] = 0;
+	ip6->payload_len = htons(payload_len);
+	ip6->nexthdr = nextheader;
+	ip6->hop_limit = hoplimit;
+	net_copy_ip6(&ip6->saddr, src);
+	net_copy_ip6(&ip6->daddr, dest);
+
+	return sizeof(struct ip6_hdr);
+}
+
+void net_ip6_handler(struct ethernet_hdr *et, struct ip6_hdr *ip6, int len)
+{
+	struct in_addr zero_ip = {.s_addr = 0 };
+	struct icmp6hdr *icmp;
+	struct udp_hdr *udp;
+	__u16 csum;
+	__u16 hlen;
+
+	if (len < IP6_HDR_SIZE)
+		return;
+
+	if (ip6->version != 6)
+		return;
+
+	switch (ip6->nexthdr) {
+	case IPPROTO_ICMPV6:
+		icmp = (struct icmp6hdr *)(((uchar *)ip6) + IP6_HDR_SIZE);
+		csum = icmp->icmp6_cksum;
+		hlen = ntohs(ip6->payload_len);
+		icmp->icmp6_cksum = 0;
+		/* checksum */
+		icmp->icmp6_cksum = csum_ipv6_magic(&ip6->saddr, &ip6->daddr,
+						    hlen, IPPROTO_ICMPV6,
+						    csum_partial((__u8 *)icmp, hlen, 0));
+		if (icmp->icmp6_cksum != csum)
+			return;
+
+		switch (icmp->icmp6_type) {
+#ifdef CONFIG_CMD_PING6
+		case IPV6_ICMP_ECHO_REQUEST:
+		case IPV6_ICMP_ECHO_REPLY:
+			ping6_receive(et, ip6, len);
+			break;
+#endif /* CONFIG_CMD_PING6 */
+
+		case IPV6_NDISC_NEIGHBOUR_SOLICITATION:
+		case IPV6_NDISC_NEIGHBOUR_ADVERTISEMENT:
+			ndisc_receive(et, ip6, len);
+			break;
+
+		default:
+			return;
+			break;
+		}
+		break;
+
+	default:
+		return;
+		break;
+	}
+}
diff --git a/net/ping6.c b/net/ping6.c
new file mode 100644
index 0000000..aa93dfa
--- /dev/null
+++ b/net/ping6.c
@@ -0,0 +1,111 @@
+/*
+ * net/ping6.c
+ *
+ * (C) Copyright 2013 Allied Telesis Labs NZ
+ *
+ * SPDX-License-Identifier:	GPL-2.0+
+ */
+#define DEBUG
+#include <common.h>
+#include <net.h>
+#include <net6.h>
+#include "ndisc.h"
+
+static ushort seq_no;
+
+/* the ipv6 address to ping */
+struct in6_addr net_ping_ip6;
+
+int
+ip6_make_ping(uchar *eth_dst_addr, struct in6_addr *neigh_addr, uchar *pkt)
+{
+	struct echo_msg *msg;
+	__u16 len;
+	uchar *pkt_old = pkt;
+
+	len = sizeof(struct echo_msg);
+
+	pkt += net_set_ether(pkt, eth_dst_addr, PROT_IP6);
+	pkt += ip6_add_hdr(pkt, &net_ip6, neigh_addr, IPPROTO_ICMPV6,
+			   IPV6_NDISC_HOPLIMIT, len);
+
+	/* ICMPv6 - Echo */
+	msg = (struct echo_msg *)pkt;
+	msg->icmph.icmp6_type = IPV6_ICMP_ECHO_REQUEST;
+	msg->icmph.icmp6_code = 0;
+	msg->icmph.icmp6_cksum = 0;
+	msg->icmph.icmp6_identifier = 0;
+	msg->icmph.icmp6_sequence = htons(seq_no++);
+	msg->id = msg->icmph.icmp6_identifier;	/* these seem redundant */
+	msg->sequence = msg->icmph.icmp6_sequence;
+
+	/* checksum */
+	msg->icmph.icmp6_cksum = csum_ipv6_magic(&net_ip6, neigh_addr, len,
+						 IPPROTO_ICMPV6,
+						 csum_partial((__u8 *)msg, len, 0));
+
+	pkt += len;
+
+	return pkt - pkt_old;
+}
+
+int ping6_send(void)
+{
+	uchar *pkt;
+	static uchar mac[6];
+
+	/* always send neighbor solicit */
+
+	memcpy(mac, net_null_ethaddr, 6);
+
+	net_nd_sol_packet_ip6 = net_ping_ip6;
+	net_nd_packet_mac = mac;
+
+	pkt = net_nd_tx_packet;
+	pkt += ip6_make_ping(mac, &net_ping_ip6, pkt);
+
+	/* size of the waiting packet */
+	net_nd_tx_packet_size = (pkt - net_nd_tx_packet);
+
+	/* and do the ARP request */
+	net_nd_try = 1;
+	net_nd_timer_start = get_timer(0);
+	ip6_NDISC_Request();
+	return 1;		/* waiting */
+}
+
+static void ping6_timeout(void)
+{
+	eth_halt();
+	net_set_state(NETLOOP_FAIL);	/* we did not get the reply */
+}
+
+void ping6_start(void)
+{
+	printf("Using %s device\n", eth_get_name());
+	net_set_timeout_handler(10000UL, ping6_timeout);
+
+	ping6_send();
+}
+
+void ping6_receive(struct ethernet_hdr *et, struct ip6_hdr *ip6, int len)
+{
+	struct icmp6hdr *icmp =
+	    (struct icmp6hdr *)(((uchar *)ip6) + IP6_HDR_SIZE);
+	struct in6_addr src_ip;
+
+	switch (icmp->icmp6_type) {
+	case IPV6_ICMP_ECHO_REPLY:
+		src_ip = ip6->saddr;
+		if (memcmp(&net_ping_ip6, &src_ip, sizeof(struct in6_addr)) != 0)
+			return;
+		net_set_state(NETLOOP_SUCCESS);
+		break;
+	case IPV6_ICMP_ECHO_REQUEST:
+		debug("Got ICMPv6 ECHO REQUEST from %pI6c\n", &ip6->saddr);
+		/* ignore for now.... */
+		break;
+	default:
+		debug("Unexpected ICMPv6 type 0x%x\n", icmp->icmp6_type);
+	}
+}
-- 
2.5.3

^ permalink raw reply related	[flat|nested] 31+ messages in thread

* [U-Boot] [RFC PATCH 6/8] net: TFTP over IPv6
  2015-10-12  7:43 [U-Boot] [RFC PATCH 0/8] IPv6 support Chris Packham
                   ` (4 preceding siblings ...)
  2015-10-12  7:43 ` [U-Boot] [RFC PATCH 5/8] net: ipv6 support Chris Packham
@ 2015-10-12  7:43 ` Chris Packham
  2015-11-02 20:43   ` Joe Hershberger
  2015-10-12  7:43 ` [U-Boot] [RFC PATCH 7/8] net: IPv6 documentation Chris Packham
                   ` (3 subsequent siblings)
  9 siblings, 1 reply; 31+ messages in thread
From: Chris Packham @ 2015-10-12  7:43 UTC (permalink / raw)
  To: u-boot

Add support for UDP/TFTP over IPv6.

Signed-off-by: Chris Packham <judge.packham@gmail.com>
---
One problem with the [hostIpAddr:]fileName syntax is that IPv6 addresses
contains colons. So tftp_start() would be confused by 'tftpboot6
$loadaddr 2001:db8::1:zImage'. It is probably possible to change the
parsing to separate the host from the filename by parsing from the end
(i.e. use strrchr() instead of strchr()) but then there are error cases
that may not be handled correctly (e.g. omitting the filename).

 common/cmd_net.c | 13 ++++++++++++
 include/net6.h   |  4 ++++
 net/net.c        |  3 +++
 net/net6.c       | 64 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 net/tftp.c       | 37 ++++++++++++++++++++++++++++++++
 5 files changed, 121 insertions(+)

diff --git a/common/cmd_net.c b/common/cmd_net.c
index 271f91d..3541599 100644
--- a/common/cmd_net.c
+++ b/common/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] [bootfilename]"
+);
+#endif
+
 #ifdef CONFIG_CMD_TFTPPUT
 int do_tftpput(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])
 {
diff --git a/include/net6.h b/include/net6.h
index a0374df..df6d38e 100644
--- a/include/net6.h
+++ b/include/net6.h
@@ -264,6 +264,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 349a18e..9e682b4 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 2315704..f5e272a 100644
--- a/net/net6.c
+++ b/net/net6.c
@@ -322,6 +322,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 + IP6_UDPHDR_SIZE);
+	/* checksum */
+	udp->udp_xsum = 0;
+	udp->udp_xsum = csum_ipv6_magic(&net_ip6, dest, len + IP6_UDPHDR_SIZE,
+		IPPROTO_UDP, csum_partial((__u8 *)udp, len + IP6_UDPHDR_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 + IP6_UDPHDR_SIZE);
+		memcpy(pkt, (uchar *)udp, len + IP6_UDPHDR_SIZE);
+
+		/* size of the waiting packet */
+		net_nd_tx_packet_size = (pkt - net_nd_tx_packet) + IP6_UDPHDR_SIZE + len;
+
+		/* and do the neighbor solicitation */
+		net_nd_try = 1;
+		net_nd_timer_start = get_timer(0);
+		ip6_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 + IP6_UDPHDR_SIZE);
+	(void) eth_send(net_tx_packet, (pkt - net_tx_packet) + IP6_UDPHDR_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 };
@@ -368,6 +412,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 +
+					IP6_UDPHDR_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 1a51131..1463bf2 100644
--- a/net/tftp.c
+++ b/net/tftp.c
@@ -10,6 +10,7 @@
 #include <command.h>
 #include <mapmem.h>
 #include <net.h>
+#include <net6.h>
 #include <net/tftp.h>
 #include "bootp.h"
 #ifdef CONFIG_SYS_DIRECT_FLASH_TFTP
@@ -94,6 +95,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
@@ -129,6 +134,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;
@@ -341,6 +348,11 @@ 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 + IP6_UDPHDR_SIZE;
+	else
+#endif
 	pkt = net_tx_packet + net_eth_hdr_size() + IP_UDP_HDR_SIZE;
 
 	switch (tftp_state) {
@@ -440,6 +452,12 @@ static void tftp_send(void)
 		break;
 	}
 
+#ifdef CONFIG_CMD_NET6
+	if (tftp6_active)
+		net_send_udp_packet6(net_server_ethaddr, &net_server_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);
 }
@@ -747,6 +765,16 @@ void tftp_start(enum proto_t protocol)
 	}
 
 	printf("Using %s device\n", eth_get_name());
+#ifdef CONFIG_CMD_NET6
+	tftp6_active = (protocol == TFTP6);
+	if (tftp6_active) {
+		printf("TFTP from server %pI6c; our IP address is %pI6c",
+		       &net_server_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",
@@ -756,6 +784,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, &net_server_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.5.3

^ permalink raw reply related	[flat|nested] 31+ messages in thread

* [U-Boot] [RFC PATCH 7/8] net: IPv6 documentation
  2015-10-12  7:43 [U-Boot] [RFC PATCH 0/8] IPv6 support Chris Packham
                   ` (5 preceding siblings ...)
  2015-10-12  7:43 ` [U-Boot] [RFC PATCH 6/8] net: TFTP over IPv6 Chris Packham
@ 2015-10-12  7:43 ` Chris Packham
  2015-11-02 20:43   ` Joe Hershberger
  2015-10-12  7:43 ` [U-Boot] [RFC PATCH 8/8] net: e1000 enable multicast reception Chris Packham
                   ` (2 subsequent siblings)
  9 siblings, 1 reply; 31+ messages in thread
From: Chris Packham @ 2015-10-12  7:43 UTC (permalink / raw)
  To: u-boot

Signed-off-by: Chris Packham <judge.packham@gmail.com>
---

 README          |  3 +++
 doc/README.ipv6 | 34 ++++++++++++++++++++++++++++++++++
 2 files changed, 37 insertions(+)
 create mode 100644 doc/README.ipv6

diff --git a/README b/README
index c22b60b..6118de2 100644
--- a/README
+++ b/README
@@ -1116,6 +1116,7 @@ The following options need to be configured:
 		CONFIG_CMD_MTDPARTS	* MTD partition support
 		CONFIG_CMD_NAND		* NAND support
 		CONFIG_CMD_NET		  bootp, tftpboot, rarpboot
+		CONFIG_CMD_NET6		* tftpboot6
 		CONFIG_CMD_NFS		  NFS support
 		CONFIG_CMD_PCA953X	* PCA953x I2C gpio commands
 		CONFIG_CMD_PCA953X_INFO * PCA953x I2C gpio info command
@@ -1123,6 +1124,8 @@ The following options need to be configured:
 		CONFIG_CMD_PCMCIA		* PCMCIA support
 		CONFIG_CMD_PING		* send ICMP ECHO_REQUEST to network
 					  host
+		CONFIG_CMD_PING6	* send ICMPv6 ECHO_REQUEST to network
+					  host
 		CONFIG_CMD_PORTIO	* Port I/O
 		CONFIG_CMD_READ		* Read raw data from partition
 		CONFIG_CMD_REGINFO	* Register dump
diff --git a/doc/README.ipv6 b/doc/README.ipv6
new file mode 100644
index 0000000..d4f738f
--- /dev/null
+++ b/doc/README.ipv6
@@ -0,0 +1,34 @@
+IPv6 Support in U-boot
+----------------------
+IPv6 support in U-boot can be considered experimental. The commands
+currently supported are tftpboot6 and ping6.
+
+The following environment variables are used
+- ip6addr - IPv6 address of the device
+- prefixlength6 - The prefix length of the IPv6 subnet (equivalent to
+  netmask for IPv4)
+- gatewayip6 - IPv6 address of the default gateway
+- serverip6 - IPv6 of the tftp server
+
+Configuration
+-------------
+The following configuration option needs to be selected to support IPv6.
+- CONFIG_CMD_NET6
+Optionally the following can also be selected to enable the ping6
+command.
+- CONFIG_CMD_PING6
+
+TFTP Server Configuration
+-------------------------
+At the time of writing U-boot has been tested against tftp-hpa
+(https://www.kernel.org/pub/software/network/tftp/) the default Debian
+package sets TFTP_ADDRESS=0.0.0.0:69 (in /etc/default/tftpd-hpa) to
+support both IPv4 and IPv6 this need to be changed to ':69'.
+
+Ethernet Driver Requirements
+----------------------------
+For IPv6 to operate correctly the Ethernet device needs to support
+transmission and reception of L2 multicast packets. Transmission is
+usually not a problem. To receive multicast packets the driver needs to
+enable promiscuous mode (some devices have the option of just enabling
+promiscuous multicast reception).
-- 
2.5.3

^ permalink raw reply related	[flat|nested] 31+ messages in thread

* [U-Boot] [RFC PATCH 8/8] net: e1000 enable multicast reception
  2015-10-12  7:43 [U-Boot] [RFC PATCH 0/8] IPv6 support Chris Packham
                   ` (6 preceding siblings ...)
  2015-10-12  7:43 ` [U-Boot] [RFC PATCH 7/8] net: IPv6 documentation Chris Packham
@ 2015-10-12  7:43 ` Chris Packham
  2015-11-02 20:44   ` Joe Hershberger
  2015-10-12  8:36 ` [U-Boot] [RFC PATCH 0/8] IPv6 support Jean-Pierre Tosoni
  2015-11-02 20:41 ` Joe Hershberger
  9 siblings, 1 reply; 31+ messages in thread
From: Chris Packham @ 2015-10-12  7:43 UTC (permalink / raw)
  To: u-boot

IPv6 neighbor discovery uses various multicast addresses to send the
request and receive the response. For neighbor discovery to work
properly in U-boot the Ethernet device needs to support joining/leaving
various L2 multicast groups or it needs to support multicast/promiscuous
mode. For the sake of simplicity the latter approach has been taken. The
e1000 hardware has slightly finer grained control in that it is possible
to enable support for multicast-promiscuous mode separately from unicast
so the extra traffic received is less.

Signed-off-by: Chris Packham <judge.packham@gmail.com>
---
Drivers that support multicast reception have it enabled/disabled with
CONFIG_MCAST_TFTP. It wouldn't be too hard to create a separate
CONFIG_MCAST that is selected by enabling CONFIG_MCAST_TFTP or
CONFIG_NET6.

 drivers/net/e1000.c | 5 +++++
 1 file changed, 5 insertions(+)

diff --git a/drivers/net/e1000.c b/drivers/net/e1000.c
index 2ba03ed..e5b00a4 100644
--- a/drivers/net/e1000.c
+++ b/drivers/net/e1000.c
@@ -5090,6 +5090,11 @@ e1000_setup_rctl(struct e1000_hw *hw)
 	rctl &= ~(E1000_RCTL_SZ_4096);
 		rctl |= E1000_RCTL_SZ_2048;
 		rctl &= ~(E1000_RCTL_BSEX | E1000_RCTL_LPE);
+
+#ifdef CONFIG_CMD_NET6
+	rctl |= E1000_RCTL_MPE;
+#endif
+
 	E1000_WRITE_REG(hw, RCTL, rctl);
 }
 
-- 
2.5.3

^ permalink raw reply related	[flat|nested] 31+ messages in thread

* [U-Boot] [RFC PATCH 0/8] IPv6 support
  2015-10-12  7:43 [U-Boot] [RFC PATCH 0/8] IPv6 support Chris Packham
                   ` (7 preceding siblings ...)
  2015-10-12  7:43 ` [U-Boot] [RFC PATCH 8/8] net: e1000 enable multicast reception Chris Packham
@ 2015-10-12  8:36 ` Jean-Pierre Tosoni
  2015-11-02 20:41 ` Joe Hershberger
  9 siblings, 0 replies; 31+ messages in thread
From: Jean-Pierre Tosoni @ 2015-10-12  8:36 UTC (permalink / raw)
  To: u-boot

Chris,

Thanks for the update. I didn't receive all the patches but well, a couple
of comments below.

> -----Message d'origine-----
> De?: Chris Packham [mailto:judge.packham at gmail.com]
> Envoy??: lundi 12 octobre 2015 09:43
> ??: u-boot at lists.denx.de; Joe Hershberger
> Cc?: jp.tosoni at acksys.fr; hannah at marvell.com; Angga; Chris Packham; Tom
> Rini; Simon Glass; Masahiro Yamada
> Objet?: [RFC PATCH 0/8] IPv6 support
> 
> This series adds basic IPv6 support to U-boot. It is a reboot of my
> earlier work on this[1]. Technically this is v4 of the series but with the
> amount of time that has passed, rebasing and running though checkpatch as
> well as the support for TFTP I've decided to reset the counter back to 1.
> 
> Most of this is ported from Allied Telesis' additions to u-boot[2].
> (Note that I am employed by Allied Telesis[3]). The hard work was done
> some time ago by Angga, I've cleaned it up and made some improvements with
> the hope of getting it accepted upstream.
> 
> A few open issues/questions
> 
> 1) Presumably the majority of the actual V6 code would be included by a
> config option. How far should I take that? Should the vsprintf code be
> conditional? I'm also not sure where to draw the line between
> CONFIG_NET6 and CONFIG_CMD_NET6, CONFIG_NET and CONFIG_CMD_NET look
> similarly confused.

If possible vsprintf code should be a different conditional since it could
be
used for other purpose than in IPv6 frames processing; ie. CONFIG_FORMAT_IP6
?

> 
> 2) This code parallels net.c and net.h. Should I continue this for the
> final version or integrate it into net.[ch].

Net.c is already big and most of your changes look independent. I would keep
net6.c separate. Maybe in an ideal future, split net.c into net4.c and net.c
?

> (...)

Regards,
Jean-Pierre Tosoni

^ permalink raw reply	[flat|nested] 31+ messages in thread

* [U-Boot] [RFC PATCH 1/8] Initial net6.h
  2015-10-12  7:43 ` [U-Boot] [RFC PATCH 1/8] Initial net6.h Chris Packham
@ 2015-11-02 20:32   ` Joe Hershberger
  0 siblings, 0 replies; 31+ messages in thread
From: Joe Hershberger @ 2015-11-02 20:32 UTC (permalink / raw)
  To: u-boot

Hi Chris,

On Mon, Oct 12, 2015 at 2:43 AM, Chris Packham <judge.packham@gmail.com> wrote:
> The initial net6.h just has the definition of an IPv6 address and IPv6
> header. Subsequent changes will build on this.
>
> Signed-off-by: Chris Packham <judge.packham@gmail.com>
> ---
> It may make sense to include this in net.h but for now I haven't done
> so.

I think it's good to keep it separate to start.

>  include/net6.h | 48 ++++++++++++++++++++++++++++++++++++++++++++++++
>  1 file changed, 48 insertions(+)
>  create mode 100644 include/net6.h
>
> diff --git a/include/net6.h b/include/net6.h
> new file mode 100644
> index 0000000..b622951
> --- /dev/null
> +++ b/include/net6.h
> @@ -0,0 +1,48 @@
> +/**
> + * Simple IPv6 network layer implementation.
> + *
> + * Based and/or adapted from the IPv4 network layer in net.[hc]
> + *
> + * (C) Copyright 2013 Allied Telesis Labs NZ
> + *
> + * SPDX-License-Identifier:    GPL-2.0+
> + */
> +#ifndef __NET6_H__
> +#define __NET6_H__
> +
> +struct in6_addr {
> +       union {
> +               __u8    u6_addr8[16];
> +               __be16  u6_addr16[8];
> +               __be32  u6_addr32[4];
> +       } in6_u;
> +
> +#define s6_addr                in6_u.u6_addr8
> +#define s6_addr16      in6_u.u6_addr16
> +#define s6_addr32      in6_u.u6_addr32
> +};
> +
> +/**
> + * struct ipv6hdr - Internet Protocol V6 (IPv6) header.
> + *
> + * IPv6 packet header as defined in RFC 2460.
> + */
> +struct ip6_hdr {
> +#if defined(__LITTLE_ENDIAN_BITFIELD)
> +       __u8    priority:4,
> +               version:4;
> +#elif defined(__BIG_ENDIAN_BITFIELD)
> +       __u8    version:4,
> +               priority:4;
> +#else
> +#error  "Please fix <asm/byteorder.h>"
> +#endif
> +       __u8            flow_lbl[3];
> +       __be16          payload_len;
> +       __u8            nexthdr;
> +       __u8            hop_limit;
> +       struct in6_addr saddr;
> +       struct in6_addr daddr;
> +};
> +
> +#endif /* __NET6_H__ */
> --
> 2.5.3
>
> _______________________________________________
> U-Boot mailing list
> U-Boot at lists.denx.de
> http://lists.denx.de/mailman/listinfo/u-boot

^ permalink raw reply	[flat|nested] 31+ messages in thread

* [U-Boot] [RFC PATCH 0/8] IPv6 support
  2015-10-12  7:43 [U-Boot] [RFC PATCH 0/8] IPv6 support Chris Packham
                   ` (8 preceding siblings ...)
  2015-10-12  8:36 ` [U-Boot] [RFC PATCH 0/8] IPv6 support Jean-Pierre Tosoni
@ 2015-11-02 20:41 ` Joe Hershberger
  9 siblings, 0 replies; 31+ messages in thread
From: Joe Hershberger @ 2015-11-02 20:41 UTC (permalink / raw)
  To: u-boot

Hi Chris,

On Mon, Oct 12, 2015 at 2:43 AM, Chris Packham <judge.packham@gmail.com> wrote:
> This series adds basic IPv6 support to U-boot. It is a reboot of my
> earlier work on this[1]. Technically this is v4 of the series but with
> the amount of time that has passed, rebasing and running though
> checkpatch as well as the support for TFTP I've decided to reset the
> counter back to 1.
>
> Most of this is ported from Allied Telesis' additions to u-boot[2].
> (Note that I am employed by Allied Telesis[3]). The hard work was done
> some time ago by Angga, I've cleaned it up and made some improvements
> with the hope of getting it accepted upstream.

Thanks for all your work on this!

> A few open issues/questions
>
> 1) Presumably the majority of the actual V6 code would be included by a
> config option. How far should I take that? Should the vsprintf code be
> conditional? I'm also not sure where to draw the line between
> CONFIG_NET6 and CONFIG_CMD_NET6, CONFIG_NET and CONFIG_CMD_NET look
> similarly confused.

They are, and I'm trying to tease them apart gradually. I'm using the
rule of thumb that if it is needed to support network traffic
independent of the command line (maybe from a standalone app or some
firmware update thing) then use CONFIG_NET, if it's only adding
command-line net features, then use CONFIG_CMD_NET. It's mostly
confused because in the past only CONFIG_CMD_NET existed to switch all
behavior on and off.

> 2) This code parallels net.c and net.h. Should I
> continue this for the final version or integrate it into net.[ch].

I think it's good to keep them separate for now. A later refactoring
can change that more easily than pulling them apart.

> 3) rxhand_f currently takes an struct in_addr *. TFTP doesn't use this
> (I haven't looked at other users). To support V6 this may need to be a
> new union, a void * with some kind of flag or nothing if no rxhandler
> actually cares.

I'm OK with removing the parameter and reparsing the packet in the
handler if needed. Again, that can be refactored later if it becomes a
pain.

> Regards,
> Chris
> --
> [1] - http://thread.gmane.org/gmane.comp.boot-loaders.u-boot/151390
> [2] - http://www.alliedtelesis.co.nz/support/gpl/other.html
> [3] - some of this has been done on work time, other parts have been
>       done in my personal time. Since I'm subscribed to the list using
>       my gmail account I've signed using that.
>
>
>
> Chris Packham (8):
>   Initial net6.h
>   lib: vsprintf: add IPv6 compressed format %pI6c
>   lib: net_utils: make string_to_ip stricter
>   lib: net_utils: add string_to_ip6
>   net: ipv6 support
>   net: TFTP over IPv6
>   net: IPv6 documentation
>   net: e1000 enable multicast reception
>
>  README                 |   3 +
>  common/Kconfig         |  15 ++
>  common/cmd_net.c       |  41 +++++
>  doc/README.ipv6        |  34 ++++
>  drivers/net/e1000.c    |   5 +
>  include/env_callback.h |   9 +
>  include/env_flags.h    |  10 ++
>  include/net.h          |   5 +-
>  include/net6.h         | 280 +++++++++++++++++++++++++++++++
>  lib/net_utils.c        | 132 ++++++++++++++-
>  lib/vsprintf.c         | 144 +++++++++++++---
>  net/Kconfig            |   5 +
>  net/Makefile           |   3 +
>  net/ndisc.c            | 269 ++++++++++++++++++++++++++++++
>  net/ndisc.h            |  27 +++
>  net/net.c              |  39 ++++-
>  net/net6.c             | 439 +++++++++++++++++++++++++++++++++++++++++++++++++
>  net/ping6.c            | 111 +++++++++++++
>  net/tftp.c             |  37 +++++
>  19 files changed, 1583 insertions(+), 25 deletions(-)
>  create mode 100644 doc/README.ipv6
>  create mode 100644 include/net6.h
>  create mode 100644 net/ndisc.c
>  create mode 100644 net/ndisc.h
>  create mode 100644 net/net6.c
>  create mode 100644 net/ping6.c
>
> --
> 2.5.3
>
> _______________________________________________
> U-Boot mailing list
> U-Boot at lists.denx.de
> http://lists.denx.de/mailman/listinfo/u-boot

^ permalink raw reply	[flat|nested] 31+ messages in thread

* [U-Boot] [RFC PATCH 2/8] lib: vsprintf: add IPv6 compressed format %pI6c
  2015-10-12  7:43 ` [U-Boot] [RFC PATCH 2/8] lib: vsprintf: add IPv6 compressed format %pI6c Chris Packham
@ 2015-11-02 20:42   ` Joe Hershberger
  0 siblings, 0 replies; 31+ messages in thread
From: Joe Hershberger @ 2015-11-02 20:42 UTC (permalink / raw)
  To: u-boot

Hi Chris,

On Mon, Oct 12, 2015 at 2:43 AM, Chris Packham <judge.packham@gmail.com> wrote:
> Add support for "human friendly" IPv6 address representations as
> specified in
> http://tools.ietf.org/html/draft-ietf-6man-text-addr-representation-00
>
> This code has been adapted from Linux kernel with minimal modification.
>
> Signed-off-by: Chris Packham <judge.packham@gmail.com>
> ---

Seems reasonable to me. It should be behind a #ifdef CONFIG_NET6 check, though.

Acked-by: Joe Hershberger <joe.hershberger@ni.com>

^ permalink raw reply	[flat|nested] 31+ messages in thread

* [U-Boot] [RFC PATCH 3/8] lib: net_utils: make string_to_ip stricter
  2015-10-12  7:43 ` [U-Boot] [RFC PATCH 3/8] lib: net_utils: make string_to_ip stricter Chris Packham
@ 2015-11-02 20:42   ` Joe Hershberger
  2015-11-09  2:49   ` Chris Packham
  1 sibling, 0 replies; 31+ messages in thread
From: Joe Hershberger @ 2015-11-02 20:42 UTC (permalink / raw)
  To: u-boot

Hi Chris,

On Mon, Oct 12, 2015 at 2:43 AM, Chris Packham <judge.packham@gmail.com> wrote:
> Previously values greater than 255 were implicitly truncated. Add some
> stricter checking to reject addresses with components >255.
>
> With the input "1234192.168.1.1" the old behaviour would truncate the
> address to 192.168.1.1. New behaviour rejects the string outright and
> returns 0.0.0.0, which for the purposes of IP addresses can be
> considered an error.
>
> Signed-off-by: Chris Packham <judge.packham@gmail.com>
> ---

Acked-by: Joe Hershberger

^ permalink raw reply	[flat|nested] 31+ messages in thread

* [U-Boot] [RFC PATCH 4/8] lib: net_utils: add string_to_ip6
  2015-10-12  7:43 ` [U-Boot] [RFC PATCH 4/8] lib: net_utils: add string_to_ip6 Chris Packham
@ 2015-11-02 20:43   ` Joe Hershberger
  0 siblings, 0 replies; 31+ messages in thread
From: Joe Hershberger @ 2015-11-02 20:43 UTC (permalink / raw)
  To: u-boot

Hi Chris,

On Mon, Oct 12, 2015 at 2:43 AM, Chris Packham <judge.packham@gmail.com> wrote:
> string_to_ip6 parses an IPv6 address from a string. Parsing v6 addresses
> is a bit more complicated than parsing v4 because there are a number of
> different formats that can be used.
>
> Signed-off-by: Chris Packham <judge.packham@gmail.com>
> ---
> I'm sure the parsing can be better and done in less code with only a
> single pass but I haven't yet figured it out. The main problem is that
> "::" can represent a variable number of contiguous "0000:" so when
> parsing "::" we can't tell how many half words to skip.
>
>  include/net6.h  |   3 ++
>  lib/net_utils.c | 119 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++
>  2 files changed, 122 insertions(+)
>
> diff --git a/include/net6.h b/include/net6.h
> index 1b82c25..a41eb87 100644
> --- a/include/net6.h
> +++ b/include/net6.h
> @@ -58,4 +58,7 @@ static inline int ipv6_addr_is_isatap(const struct in6_addr *a)
>         return (a->s6_addr32[2] | htonl(0x02000000)) == htonl(0x02005EFE);
>  }
>
> +/* Convert a string to an ipv6 address */
> +int string_to_ip6(const char *s, struct in6_addr *addr);
> +
>  #endif /* __NET6_H__ */
> diff --git a/lib/net_utils.c b/lib/net_utils.c
> index 0fca54d..b0d0364 100644
> --- a/lib/net_utils.c
> +++ b/lib/net_utils.c
> @@ -11,6 +11,8 @@
>   */
>
>  #include <common.h>
> +#include <net6.h>
> +#include <linux/ctype.h>
>
>  struct in_addr string_to_ip(const char *s)
>  {
> @@ -43,3 +45,120 @@ struct in_addr string_to_ip(const char *s)
>         addr.s_addr = htonl(addr.s_addr);
>         return addr;
>  }
> +
> +/**
> + * Parses an struct in6_addr from the given string. IPv6 address parsing is a bit
> + * more complicated than v4 due to the flexible format and some of the special
> + * cases (e.g. v4 mapped).
> + *
> + * Examples of valid strings:
> + *   2001:db8::0:1234:1
> + *   2001:0db8:0000:0000:0000:0000:1234:0001
> + *   ::1
> + *   ::ffff:192.168.1.1
> + *
> + * Examples of invalid strings
> + *   2001:db8::0::0          (:: can only appear once)
> + *   2001:db8:192.168.1.1::1 (v4 part can only appear at the end)
> + *   192.168.1.1             (we don't implicity map v4)
> + */
> +int string_to_ip6(const char *strpt, struct in6_addr *addrpt)
> +{
> +       int colon_count = 0;
> +       int found_double_colon = 0;
> +       int xstart = 0;         /* first zero (double colon) */
> +       int len = 7;            /* num words the double colon represents */
> +       int i;
> +       const char *s = strpt;
> +       struct in_addr zero_ip = {.s_addr = 0};
> +
> +       if (strpt == NULL)
> +               return -1;
> +
> +       /* First pass, verify the syntax and locate the double colon */
> +       for (;;) {
> +               while (isxdigit((int)*s))
> +                       s++;
> +               if (*s == '\0')
> +                       break;
> +               if (*s != ':') {
> +                       if (*s == '.' && len >= 2) {
> +                               struct in_addr v4;
> +                               while (s != strpt && *(s - 1) != ':')
> +                                       --s;
> +                               v4 = string_to_ip(s);
> +                               if (memcmp(&zero_ip, &v4,
> +                                          sizeof(struct in_addr) != 0)) {
> +                                       len -= 2;
> +                                       break;
> +                               }
> +                       }
> +                       /* This could be a valid address */
> +                       break;
> +               }
> +               if (s == strpt) {
> +                       /* The address begins with a colon */
> +                       if (*++s != ':')
> +                               /* Must start with a double colon or a number */
> +                               goto out_err;
> +               } else {
> +                       s++;
> +                       if (found_double_colon)
> +                               len--;
> +                       else
> +                               xstart++;
> +               }
> +
> +               if (*s == ':') {
> +                       if (found_double_colon)
> +                               /* Two double colons are not allowed */
> +                               goto out_err;
> +                       found_double_colon = 1;
> +                       len -= xstart;
> +                       s++;
> +               }
> +
> +               if (++colon_count == 7)
> +                       /* Found all colons */
> +                       break;
> +       }
> +
> +       if (colon_count == 0 || colon_count > 7)
> +               goto out_err;

Above you bail out if colon_count == 7 so how could it be greater
here? My guess is you need to use strchr() to check for additional
colons in the remaining string.

> +       if (*--s == ':')
> +               len++;
> +
> +       /* Second pass, read the address */
> +       s = strpt;
> +       for (i = 0; i < 8; i++) {
> +               int val = 0;
> +               char *end;
> +
> +               if (found_double_colon && i >= xstart && i < xstart + len) {
> +                       addrpt->s6_addr16[i] = 0;
> +                       continue;
> +               }
> +               while (*s == ':')
> +                       s++;
> +
> +               if (i == 6 && isdigit((int)*s)) {
> +                       struct in_addr v4 = string_to_ip(s);
> +                       if (memcmp(&zero_ip, &v4,
> +                                  sizeof(struct in_addr)) != 0) {
> +                               /* Ending with :IPv4-address */
> +                               addrpt->s6_addr32[3] = v4.s_addr;
> +                               break;
> +                       }
> +               }
> +
> +               val = simple_strtoul(s, &end, 16);
> +               if (*end != '\0' && *end != ':')
> +                       goto out_err;
> +               addrpt->s6_addr16[i] = htons(val);
> +               s = end;
> +       }
> +       return 0;
> +
> +out_err:
> +       return -1;
> +}
> --

I tend to think we can revisit this to make it more concise in the
future. Assuming this is functional, it is better to have a solution
even if it's not the perfect solution.

Cheers,
-Joe

^ permalink raw reply	[flat|nested] 31+ messages in thread

* [U-Boot] [RFC PATCH 5/8] net: ipv6 support
  2015-10-12  7:43 ` [U-Boot] [RFC PATCH 5/8] net: ipv6 support Chris Packham
@ 2015-11-02 20:43   ` Joe Hershberger
  2015-11-03 10:11     ` Chris Packham
  0 siblings, 1 reply; 31+ messages in thread
From: Joe Hershberger @ 2015-11-02 20:43 UTC (permalink / raw)
  To: u-boot

Hi Chris,

On Mon, Oct 12, 2015 at 2:43 AM, Chris Packham <judge.packham@gmail.com> wrote:
> Adds basic support for IPv6. Neighbor discovery and ping6 are the only
> things supported at the moment.
>
> Helped-by: Hanna Hawa <hannah@marvell.com> [endian & alignment fixes]
> Signed-off-by: Chris Packham <judge.packham@gmail.com>
> ---
> Now we have something functional. With this you can do something like
> 'setenv ipaddr6 3ffe::2' and 'ping6 3ffe::1' should work.
>
> I seem to have a problem that when you send a ping6 for a non-existent
> address that ends up stuck and the next non-ipv6 net operation tries to
> resolve it. I suspect this is because the pending neighbor discovery
> information isn't cleaned up properly, I need to look into that.
>
> The environment variable prefixlength6 is a bit fiddly. No-one uses a
> netmask6 (it'd mean a lot of ffff:ffff:...) it's almost always done by
> including the prefix length in the address (e.g. ip addr add
> 2001:db8::1/64) I'm contemplating adopting that syntax and dropping
> prefixlength6.
>
> This patch is bigger than I'd like it to be but I'm not sure how to
> split it up and keep the parts build able.

It is pretty huge. It's taken me a while to get through it.

>  common/Kconfig         |  15 ++
>  common/cmd_net.c       |  28 ++++
>  include/env_callback.h |   9 ++
>  include/env_flags.h    |  10 ++
>  include/net.h          |   5 +-
>  include/net6.h         | 212 ++++++++++++++++++++++++++++
>  net/Kconfig            |   5 +
>  net/Makefile           |   3 +
>  net/ndisc.c            | 269 +++++++++++++++++++++++++++++++++++
>  net/ndisc.h            |  27 ++++
>  net/net.c              |  36 ++++-
>  net/net6.c             | 375 +++++++++++++++++++++++++++++++++++++++++++++++++
>  net/ping6.c            | 111 +++++++++++++++
>  13 files changed, 1102 insertions(+), 3 deletions(-)
>  create mode 100644 net/ndisc.c
>  create mode 100644 net/ndisc.h
>  create mode 100644 net/net6.c
>  create mode 100644 net/ping6.c
>
> diff --git a/common/Kconfig b/common/Kconfig
> index 2c42b8e..c72563d 100644
> --- a/common/Kconfig
> +++ b/common/Kconfig
> @@ -389,6 +389,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

This is added in the next patch, so should probably move there.

> +
>  config CMD_TFTPPUT
>         bool "tftp put"
>         help
> @@ -420,6 +429,12 @@ config CMD_PING
>         help
>           Send ICMP ECHO_REQUEST to network host
>
> +config CMD_PING6
> +       bool "ping6"
> +       depends on CMD_NET6
> +       help
> +         Send ICMPv6 ECHO_REQUEST to network host

What makes ping inseparable from the core support?

> +
>  config CMD_CDP
>         bool "cdp"
>         help
> diff --git a/common/cmd_net.c b/common/cmd_net.c
> index b2f3c7b..271f91d 100644
> --- a/common/cmd_net.c
> +++ b/common/cmd_net.c
> @@ -11,6 +11,7 @@
>  #include <common.h>
>  #include <command.h>
>  #include <net.h>
> +#include <net6.h>
>
>  static int netboot_common(enum proto_t, cmd_tbl_t *, int, char * const []);
>
> @@ -284,6 +285,33 @@ U_BOOT_CMD(
>  );
>  #endif
>
> +#ifdef CONFIG_CMD_PING6
> +int do_ping6(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])
> +{
> +       if (argc < 2)
> +               return -1;
> +
> +       if (string_to_ip6(argv[1], &net_ping_ip6) != 0)
> +               return CMD_RET_USAGE;
> +
> +       if (net_loop(PING6) < 0) {
> +               printf("ping6 failed; host %pI6c is not alive\n",
> +                      &net_ping_ip6);
> +               return 1;
> +       }
> +
> +       printf("host %pI6c is alive\n", &net_ping_ip6);
> +
> +       return 0;
> +}
> +
> +U_BOOT_CMD(
> +       ping6,  2,      1,      do_ping6,
> +       "send ICMPv6 ECHO_REQUEST to network host",
> +       "pingAddress"
> +);
> +#endif /* CONFIG_CMD_PING6 */
> +
>  #if defined(CONFIG_CMD_CDP)
>
>  static void cdp_update_env(void)
> diff --git a/include/env_callback.h b/include/env_callback.h
> index 90b95b5..9027f3f 100644
> --- a/include/env_callback.h
> +++ b/include/env_callback.h
> @@ -60,6 +60,14 @@
>  #define NET_CALLBACKS
>  #endif
>
> +#ifdef CONFIG_NET6
> +#define NET6_CALLBACKS \
> +       "ip6addr:ip6addr," \
> +       "serverip6:serverip6," \
> +       "prefixlength6:prefixlength6,"

I like the other nomenclature better as well (included in the address).

> +#else
> +#define NET6_CALLBACKS
> +#endif
>  /*
>   * This list of callback bindings is static, but may be overridden by defining
>   * a new association in the ".callbacks" environment variable.
> @@ -68,6 +76,7 @@
>         ENV_DOT_ESCAPE ENV_FLAGS_VAR ":flags," \
>         "baudrate:baudrate," \
>         NET_CALLBACKS \
> +       NET6_CALLBACKS \
>         "loadaddr:loadaddr," \
>         SILENT_CALLBACK \
>         SPLASHIMAGE_CALLBACK \
> diff --git a/include/env_flags.h b/include/env_flags.h
> index 8823fb9..6e1891d 100644
> --- a/include/env_flags.h
> +++ b/include/env_flags.h
> @@ -65,6 +65,15 @@ enum env_flags_varaccess {
>  #define NET_FLAGS
>  #endif
>
> +#ifdef CONFIG_CMD_NET6

I think CONFIG_NET6 would be better here.

> +#define NET6_FLAGS \
> +       "ip6addr:s," \
> +       "serverip6:s," \
> +       "prefixlength6:d,"
> +#else
> +#define NET6_FLAGS
> +#endif
> +
>  #ifndef CONFIG_ENV_OVERWRITE
>  #define SERIAL_FLAGS "serial#:so,"
>  #else
> @@ -74,6 +83,7 @@ enum env_flags_varaccess {
>  #define ENV_FLAGS_LIST_STATIC \
>         ETHADDR_FLAGS \
>         NET_FLAGS \
> +       NET6_FLAGS \
>         SERIAL_FLAGS \
>         CONFIG_ENV_FLAGS_LIST_STATIC
>
> diff --git a/include/net.h b/include/net.h
> index 3a787cc..4f59609 100644
> --- a/include/net.h
> +++ b/include/net.h
> @@ -316,6 +316,7 @@ struct vlan_ethernet_hdr {
>  #define VLAN_ETHER_HDR_SIZE    (sizeof(struct vlan_ethernet_hdr))
>
>  #define PROT_IP                0x0800          /* IP protocol                  */
> +#define PROT_IP6        0x86DD          /* IPv6 protocol               */
>  #define PROT_ARP       0x0806          /* IP ARP protocol              */
>  #define PROT_RARP      0x8035          /* IP ARP protocol              */
>  #define PROT_VLAN      0x8100          /* IEEE 802.1q protocol         */
> @@ -512,8 +513,8 @@ extern ushort               net_native_vlan;        /* Our Native VLAN */
>  extern int             net_restart_wrap;       /* Tried all network devices */
>
>  enum proto_t {
> -       BOOTP, RARP, ARP, TFTPGET, DHCP, PING, DNS, NFS, CDP, NETCONS, SNTP,
> -       TFTPSRV, TFTPPUT, LINKLOCAL
> +       BOOTP, RARP, ARP, TFTPGET, DHCP, PING, PING6, DNS, NFS, CDP, NETCONS,
> +       SNTP, TFTPSRV, TFTPPUT, TFTP6, LINKLOCAL

These should be added with the patch that adds the feature (TFTP6 for
sure, and probably PING6 when it is a separate patch).

>  };
>
>  extern char    net_boot_file_name[128];/* Boot File name */
> diff --git a/include/net6.h b/include/net6.h
> index a41eb87..a0374df 100644
> --- a/include/net6.h
> +++ b/include/net6.h
> @@ -22,6 +22,16 @@ struct in6_addr {
>  #define s6_addr32      in6_u.u6_addr32
>  };
>
> +#define IN6ADDRSZ      sizeof(struct in6_addr)
> +#define INETHADDRSZ    sizeof(net_ethaddr)
> +
> +#define IPV6_ADDRSCOPE_INTF    0x01
> +#define IPV6_ADDRSCOPE_LINK    0x02
> +#define IPV6_ADDRSCOPE_AMDIN   0x04
> +#define IPV6_ADDRSCOPE_SITE    0x05
> +#define IPV6_ADDRSCOPE_ORG     0x08
> +#define IPV6_ADDRSCOPE_GLOBAL  0x0E
> +
>  /**
>   * struct ipv6hdr - Internet Protocol V6 (IPv6) header.
>   *
> @@ -45,6 +55,154 @@ struct ip6_hdr {
>         struct in6_addr daddr;
>  };
>
> +#define IP6_HDR_SIZE (sizeof(struct ip6_hdr))
> +
> +/* Handy for static initialisations of struct in6_addr, atlhough the
> + * c99 '= { 0 }' idiom might work depending on you compiler. */
> +#define ZERO_IPV6_ADDR { { { 0x00, 0x00, 0x00, 0x00, \
> +                         0x00, 0x00, 0x00, 0x00, \
> +                         0x00, 0x00, 0x00, 0x00, \
> +                         0x00, 0x00, 0x00, 0x00 } } }
> +
> +#define IPV6_LINK_LOCAL_PREFIX 0xfe80
> +
> +struct udp_hdr {
> +       __be16          udp_src;        /* UDP source port              */
> +       __be16          udp_dst;        /* UDP destination port         */
> +       __be16          udp_len;        /* Length of UDP packet         */
> +       __be16          udp_xsum;       /* Checksum                     */
> +};
> +
> +#define IP6_UDPHDR_SIZE (sizeof(struct udp_hdr))

UDP is shared between IP and IP6, right? There seems to be no reason
to call it IP6_UDPHDR_SIZE. I would go with UDP_HDR_SIZE, but of
course that is already defined. Seem like a good candidate to combine
with the existing implementation.

Maybe that means that struct ip_udp_hdr goes away or maybe it's
cleaner to keep it as well. Have to dive in to know.

> +enum {
> +       __ND_OPT_PREFIX_INFO_END        = 0,
> +       ND_OPT_SOURCE_LL_ADDR           = 1,
> +       ND_OPT_TARGET_LL_ADDR           = 2,
> +       ND_OPT_PREFIX_INFO              = 3,
> +       ND_OPT_REDIRECT_HDR             = 4,
> +       ND_OPT_MTU                      = 5,
> +       __ND_OPT_MAX
> +};
> +
> +/* ICMPv6 */
> +#define IPPROTO_ICMPV6                 58
> +/* hop limit for neighbour discovery packets */
> +#define IPV6_NDISC_HOPLIMIT             255
> +#define NDISC_TIMEOUT                  5000UL
> +#define NDISC_TIMEOUT_COUNT             3
> +
> +struct icmp6hdr {
> +       __u8    icmp6_type;
> +#define IPV6_ICMP_ECHO_REQUEST                 128
> +#define IPV6_ICMP_ECHO_REPLY                   129
> +#define IPV6_NDISC_ROUTER_SOLICITATION         133
> +#define IPV6_NDISC_ROUTER_ADVERTISEMENT                134
> +#define IPV6_NDISC_NEIGHBOUR_SOLICITATION      135
> +#define IPV6_NDISC_NEIGHBOUR_ADVERTISEMENT     136
> +#define IPV6_NDISC_REDIRECT                    137
> +       __u8    icmp6_code;
> +       __be16  icmp6_cksum;
> +
> +       union {
> +               __be32  un_data32[1];
> +               __be16  un_data16[2];
> +               __u8    un_data8[4];
> +
> +               struct icmpv6_echo {
> +                       __be16          identifier;
> +                       __be16          sequence;
> +               } u_echo;
> +
> +               struct icmpv6_nd_advt {
> +#if defined(__LITTLE_ENDIAN_BITFIELD)
> +                       __be32          reserved:5,
> +                                       override:1,
> +                                       solicited:1,
> +                                       router:1,
> +                                       reserved2:24;
> +#elif defined(__BIG_ENDIAN_BITFIELD)
> +                       __be32          router:1,
> +                                       solicited:1,
> +                                       override:1,
> +                                       reserved:29;
> +#else
> +#error "Please fix <asm/byteorder.h>"
> +#endif
> +               } u_nd_advt;
> +
> +               struct icmpv6_nd_ra {
> +                       __u8            hop_limit;
> +#if defined(__LITTLE_ENDIAN_BITFIELD)
> +                       __u8            reserved:6,
> +                                       other:1,
> +                                       managed:1;
> +
> +#elif defined(__BIG_ENDIAN_BITFIELD)
> +                       __u8            managed:1,
> +                                       other:1,
> +                                       reserved:6;
> +#else
> +#error "Please fix <asm/byteorder.h>"
> +#endif
> +                       __be16          rt_lifetime;
> +               } u_nd_ra;
> +       } icmp6_dataun;
> +#define icmp6_identifier       icmp6_dataun.u_echo.identifier
> +#define icmp6_sequence         icmp6_dataun.u_echo.sequence
> +#define icmp6_pointer          icmp6_dataun.un_data32[0]
> +#define icmp6_mtu              icmp6_dataun.un_data32[0]
> +#define icmp6_unused           icmp6_dataun.un_data32[0]
> +#define icmp6_maxdelay         icmp6_dataun.un_data16[0]
> +#define icmp6_router           icmp6_dataun.u_nd_advt.router
> +#define icmp6_solicited                icmp6_dataun.u_nd_advt.solicited
> +#define icmp6_override         icmp6_dataun.u_nd_advt.override
> +#define icmp6_ndiscreserved    icmp6_dataun.u_nd_advt.reserved
> +#define icmp6_hop_limit                icmp6_dataun.u_nd_ra.hop_limit
> +#define icmp6_addrconf_managed icmp6_dataun.u_nd_ra.managed
> +#define icmp6_addrconf_other   icmp6_dataun.u_nd_ra.other
> +#define icmp6_rt_lifetime      icmp6_dataun.u_nd_ra.rt_lifetime
> +};
> +
> +struct nd_msg {
> +       struct icmp6hdr icmph;
> +       struct in6_addr target;
> +       __u8            opt[0];
> +};
> +
> +struct rs_msg {
> +       struct icmp6hdr icmph;
> +       __u8            opt[0];
> +};
> +
> +struct ra_msg {
> +       struct icmp6hdr icmph;
> +       __u32           reachable_time;
> +       __u32           retrans_timer;
> +};
> +
> +struct echo_msg {
> +       struct icmp6hdr icmph;
> +       __u16           id;
> +       __u16           sequence;
> +};
> +
> +struct nd_opt_hdr {
> +       __u8            nd_opt_type;
> +       __u8            nd_opt_len;
> +} __attribute__((__packed__));
> +
> +extern struct in6_addr const net_null_addr_ip6;        /* NULL IPv6 address */
> +extern struct in6_addr net_gateway6;   /* Our gateways IPv6 address */
> +extern struct in6_addr net_ip6;        /* Our IPv6 addr (0 = unknown) */
> +extern struct in6_addr net_link_local_ip6;     /* Our link local IPv6 addr */
> +extern u_int32_t net_prefix_length;    /* Our prefixlength (0 = unknown) */
> +extern struct in6_addr net_server_ip6; /* Server IPv6 addr (0 = unknown) */
> +
> +#ifdef CONFIG_CMD_PING
> +extern struct in6_addr net_ping_ip6;   /* the ipv6 address to ping */
> +#endif
> +
>  /* ::ffff:0:0/96 is reserved for v4 mapped addresses */
>  static inline int ipv6_addr_v4mapped(const struct in6_addr *a)
>  {
> @@ -61,4 +219,58 @@ static inline int ipv6_addr_is_isatap(const struct in6_addr *a)
>  /* Convert a string to an ipv6 address */
>  int string_to_ip6(const char *s, struct in6_addr *addr);
>
> +/* check that an IPv6 address is unspecified (zero) */
> +int ip6_is_unspecified_addr(struct in6_addr *addr);
> +
> +/* check that an IPv6 address is ours */
> +int ip6_is_our_addr(struct in6_addr *addr);
> +
> +void ip6_make_lladdr(struct in6_addr *lladr, unsigned char const enetaddr[6]);
> +
> +void ip6_make_SNMA(struct in6_addr *mcast_addr, struct in6_addr *ip6_addr);
> +
> +void ip6_make_mult_ethdstaddr(unsigned char enetaddr[6],
> +                             struct in6_addr *mcast_addr);
> +
> +/* check if neighbour is in the same subnet as us */
> +int ip6_addr_in_subnet(struct in6_addr *our_addr, struct in6_addr *neigh_addr,
> +                      __u32 prefix_length);
> +
> +unsigned int csum_partial(const unsigned char *buff, int len, unsigned int sum);
> +
> +unsigned short int csum_ipv6_magic(struct in6_addr *saddr,
> +                                  struct in6_addr *daddr, __u16 len,
> +                                  unsigned short proto, unsigned int csum);
> +
> +int ip6_add_hdr(uchar *xip, struct in6_addr *src, struct in6_addr *dest,
> +               int nextheader, int hoplimit, int payload_len);
> +
> +/* send a neighbour discovery solicitation message */
> +void ip6_NDISC_Request(void);
> +
> +/* call back routine when ND timer has gone off */
> +void ip6_NDISC_TimeoutCheck(void);
> +
> +/* initialises the ND data */
> +void ip6_NDISC_init(void);
> +
> +/* sends an IPv6 echo request to a host */
> +int ping6_send(void);
> +
> +/* starts a Ping6 process */
> +void ping6_start(void);
> +
> +/* handles reception of icmpv6 echo request/reply */
> +void ping6_receive(struct ethernet_hdr *et, struct ip6_hdr *ip6,
> +                         int len);
> +
> +/* handler for incoming IPv6 echo packet */
> +void net_ip6_handler(struct ethernet_hdr *et, struct ip6_hdr *ip6,
> +                           int len);
> +
> +/* copy IPv6 */
> +static inline void net_copy_ip6(void *to, void *from)
> +{
> +       memcpy((void *)to, from, sizeof(struct in6_addr));
> +}
>  #endif /* __NET6_H__ */
> diff --git a/net/Kconfig b/net/Kconfig
> index 77a2f7e..ee198c1 100644
> --- a/net/Kconfig
> +++ b/net/Kconfig
> @@ -22,4 +22,9 @@ config NETCONSOLE
>           Support the 'nc' input/output device for networked console.
>           See README.NetConsole for details.
>
> +config NET6
> +       bool "IPv6 support"
> +       help
> +         Support for IPv6
> +
>  endif   # if NET
> diff --git a/net/Makefile b/net/Makefile
> index e9cc8ad..a85a5c6 100644
> --- a/net/Makefile
> +++ b/net/Makefile
> @@ -20,3 +20,6 @@ obj-$(CONFIG_CMD_PING) += ping.o
>  obj-$(CONFIG_CMD_RARP) += rarp.o
>  obj-$(CONFIG_CMD_SNTP) += sntp.o
>  obj-$(CONFIG_CMD_NET)  += tftp.o
> +obj-$(CONFIG_CMD_NET6) += net6.o
> +obj-$(CONFIG_CMD_NET6) += ndisc.o

I'd like to see the stuff that is net core (not a command) start using
CONFIG_NET6 instead of CONFIG_CMD_NET6.

> +obj-$(CONFIG_CMD_PING6) += ping6.o
> diff --git a/net/ndisc.c b/net/ndisc.c
> new file mode 100644
> index 0000000..41883d7
> --- /dev/null
> +++ b/net/ndisc.c
> @@ -0,0 +1,269 @@
> +/*
> + * net/ndisc.c
> + *
> + * (C) Copyright 2013 Allied Telesis Labs NZ
> + *
> + * SPDX-License-Identifier:    GPL-2.0+
> + */
> +#define DEBUG
> +#include <common.h>
> +#include <net.h>
> +#include <net6.h>
> +#include "ndisc.h"
> +
> +/* IPv6 destination address of packet waiting for ND */
> +struct in6_addr net_nd_sol_packet_ip6 = ZERO_IPV6_ADDR;
> +/* IPv6 address we are expecting ND advert from */
> +struct in6_addr net_nd_rep_packet_ip6 = ZERO_IPV6_ADDR;

static. The same goes for any others that are not needed outside of this file.

> +/* MAC destination address of packet waiting for ND */
> +uchar *net_nd_packet_mac;
> +/* pointer to packet waiting to be transmitted after ND is resolved */
> +uchar *net_nd_tx_packet;
> +uchar net_nd_packet_buf[PKTSIZE_ALIGN + PKTALIGN];
> +/* size of packet waiting to be transmitted */
> +int net_nd_tx_packet_size;
> +/* the timer for ND resolution */
> +ulong net_nd_timer_start;
> +/* the number of requests we have sent so far */
> +int net_nd_try;
> +
> +#define IP6_NDISC_OPT_SPACE(len) (((len)+2+7)&~7)
> +
> +/**
> + * Insert an iption into a neighbor discovery packet.

iption -> option

> + * Returns the number of bytes inserted (which may be >= len)
> + */
> +static int
> +ip6_ndisc_insert_option(struct nd_msg *ndisc, int type, u8 *data, int len)
> +{
> +       int space = IP6_NDISC_OPT_SPACE(len);
> +
> +       ndisc->opt[0] = type;
> +       ndisc->opt[1] = space >> 3;
> +       memcpy(&ndisc->opt[2], data, len);
> +       len += 2;
> +
> +       /* fill the remainder with 0 */
> +       if ((space - len) > 0)
> +               memset(&ndisc->opt[len], 0, space - len);
> +
> +       return space;
> +}
> +
> +/**
> + * Extract the Ethernet address from a neighbor discovery packet.
> + * Note that the link layer address could be anything but the only networking
> + * media that u-boot supports is Ethernet so we assume we're extracting a 6
> + * byte Ethernet MAC address.
> + */
> +static void ip6_ndisc_extract_enetaddr(struct nd_msg *ndisc, uchar enetaddr[6])
> +{
> +       memcpy(enetaddr, &ndisc->opt[2], 6);
> +}
> +
> +/**
> + * Check to see if the neighbor discovery packet has
> + * the specified option set.
> + */
> +static int ip6_ndisc_has_option(struct ip6_hdr *ip6, __u8 type)
> +{
> +       struct nd_msg *ndisc = (struct nd_msg *)(((uchar *)ip6) + IP6_HDR_SIZE);
> +
> +       if (ip6->payload_len <= sizeof(struct icmp6hdr))
> +               return 0;
> +
> +       return ndisc->opt[0] == type;
> +}
> +
> +static void ip6_send_ns(struct in6_addr *neigh_addr)
> +{
> +       struct in6_addr dst_adr;
> +       unsigned char enetaddr[6];
> +       struct nd_msg *msg;
> +       __u16 len;
> +       uchar *pkt;
> +
> +       debug("sending neighbor solicitation for %pI6c our address %pI6c\n",
> +             neigh_addr, &net_link_local_ip6);
> +
> +       /* calculate src, dest IPv6 addr and dest Eth addr */
> +       ip6_make_SNMA(&dst_adr, neigh_addr);
> +       ip6_make_mult_ethdstaddr(enetaddr, &dst_adr);
> +       len = sizeof(struct icmp6hdr) + IN6ADDRSZ +
> +           IP6_NDISC_OPT_SPACE(INETHADDRSZ);
> +
> +       pkt = (uchar *)net_tx_packet;
> +       pkt += net_set_ether(pkt, enetaddr, PROT_IP6);
> +       pkt += ip6_add_hdr(pkt, &net_link_local_ip6, &dst_adr, IPPROTO_ICMPV6,
> +                          IPV6_NDISC_HOPLIMIT, len);
> +
> +       /* ICMPv6 - NS */
> +       msg = (struct nd_msg *)pkt;
> +       msg->icmph.icmp6_type = IPV6_NDISC_NEIGHBOUR_SOLICITATION;
> +       msg->icmph.icmp6_code = 0;
> +       memset(&msg->icmph.icmp6_cksum, 0, sizeof(__be16));
> +       memset(&msg->icmph.icmp6_unused, 0, sizeof(__be32));
> +
> +       /* Set the target address and llsaddr option */
> +       net_copy_ip6(&msg->target, neigh_addr);
> +       ip6_ndisc_insert_option(msg, ND_OPT_SOURCE_LL_ADDR, net_ethaddr,
> +                               INETHADDRSZ);
> +
> +       /* checksum */
> +       msg->icmph.icmp6_cksum = csum_ipv6_magic(&net_link_local_ip6, &dst_adr,
> +                                                len, IPPROTO_ICMPV6,
> +                                                csum_partial((__u8 *)msg, len, 0));
> +
> +       pkt += len;
> +
> +       /* send it! */
> +       net_send_packet(net_tx_packet, (pkt - net_tx_packet));
> +}
> +
> +static void
> +ip6_send_na(uchar *eth_dst_addr, struct in6_addr *neigh_addr,
> +           struct in6_addr *target)
> +{
> +       struct nd_msg *msg;
> +       __u16 len;
> +       uchar *pkt;
> +
> +       debug("sending neighbor advertisement for %pI6c to %pI6c (%pM)\n",
> +             target, neigh_addr, eth_dst_addr);
> +
> +       len = sizeof(struct icmp6hdr) + IN6ADDRSZ +
> +           IP6_NDISC_OPT_SPACE(INETHADDRSZ);
> +
> +       pkt = (uchar *)net_tx_packet;
> +       pkt += net_set_ether(pkt, eth_dst_addr, PROT_IP6);
> +       pkt += ip6_add_hdr(pkt, &net_link_local_ip6, neigh_addr,
> +                          IPPROTO_ICMPV6, IPV6_NDISC_HOPLIMIT, len);
> +
> +       /* ICMPv6 - NS */

Probably mean to say "ICMPv6 - NA" here, right?

> +       msg = (struct nd_msg *)pkt;
> +       msg->icmph.icmp6_type = IPV6_NDISC_NEIGHBOUR_ADVERTISEMENT;
> +       msg->icmph.icmp6_code = 0;
> +       memset(&msg->icmph.icmp6_cksum, 0, sizeof(__be16));
> +       memset(&msg->icmph.icmp6_unused, 0, sizeof(__be32));
> +
> +       /* Set the target address and lltargetaddr option */
> +       net_copy_ip6(&msg->target, target);
> +       ip6_ndisc_insert_option(msg, ND_OPT_TARGET_LL_ADDR, net_ethaddr,
> +                               INETHADDRSZ);
> +
> +       /* checksum */
> +       msg->icmph.icmp6_cksum = csum_ipv6_magic(&net_link_local_ip6,
> +                                                neigh_addr, len, IPPROTO_ICMPV6,
> +                                                csum_partial((__u8 *)msg, len, 0));
> +
> +       pkt += len;
> +
> +       /* send it! */
> +       net_send_packet(net_tx_packet, (pkt - net_tx_packet));
> +}
> +
> +void ip6_NDISC_Request(void)

Please don't use CamelCase looking stuff. All lower case is usually appropriate.

> +{
> +       if (!ip6_addr_in_subnet(&net_ip6, &net_nd_sol_packet_ip6,
> +                               net_prefix_length)) {
> +               if (ip6_is_unspecified_addr(&net_gateway6)) {
> +                       puts("## Warning: gatewayip6 is needed but not set\n");
> +                       net_nd_rep_packet_ip6 = net_nd_sol_packet_ip6;

Is this just assuming that since there is no gateway that the device
might be on the local segment even though the address is not on our
subnet?

> +               } else {
> +                       net_nd_rep_packet_ip6 = net_gateway6;
> +               }
> +       } else {
> +               net_nd_rep_packet_ip6 = net_nd_sol_packet_ip6;
> +       }
> +
> +       ip6_send_ns(&net_nd_rep_packet_ip6);
> +}
> +
> +void ip6_NDISC_TimeoutCheck(void)

Lowercase.

> +{
> +       ulong t;
> +
> +       if (ip6_is_unspecified_addr(&net_nd_sol_packet_ip6))
> +               return;
> +
> +       t = get_timer(0);
> +
> +       /* check for NDISC timeout */
> +       if ((t - net_nd_timer_start) > NDISC_TIMEOUT) {
> +               net_nd_try++;
> +               if (net_nd_try >= NDISC_TIMEOUT_COUNT) {
> +                       puts("\nNeighbour discovery retry count exceeded; "
> +                            "starting again\n");
> +                       net_nd_try = 0;
> +                       net_start_again();
> +               } else {
> +                       net_nd_timer_start = t;
> +                       ip6_NDISC_Request();
> +               }
> +       }
> +}
> +
> +void ip6_NDISC_init(void)

Lowercase.

> +{
> +       net_nd_packet_mac = NULL;
> +       net_nd_tx_packet = NULL;
> +       net_nd_sol_packet_ip6 = net_null_addr_ip6;
> +       net_nd_rep_packet_ip6 = net_null_addr_ip6;
> +       net_nd_tx_packet = NULL;
> +
> +       if (!net_nd_tx_packet) {
> +               net_nd_tx_packet = &net_nd_packet_buf[0] + (PKTALIGN - 1);
> +               net_nd_tx_packet -= (ulong)net_nd_tx_packet % PKTALIGN;
> +               net_nd_tx_packet_size = 0;
> +       }
> +}
> +
> +void ndisc_receive(struct ethernet_hdr *et, struct ip6_hdr *ip6, int len)
> +{
> +       struct icmp6hdr *icmp =
> +           (struct icmp6hdr *)(((uchar *)ip6) + IP6_HDR_SIZE);
> +       struct nd_msg *ndisc = (struct nd_msg *)icmp;
> +       uchar neigh_eth_addr[6];
> +
> +       switch (icmp->icmp6_type) {
> +       case IPV6_NDISC_NEIGHBOUR_SOLICITATION:
> +               debug("received neighbor solicitation for %pI6c from %pI6c\n",
> +                     &ndisc->target, &ip6->saddr);
> +               if (ip6_is_our_addr(&ndisc->target) &&
> +                   ip6_ndisc_has_option(ip6, ND_OPT_SOURCE_LL_ADDR)) {
> +                       ip6_ndisc_extract_enetaddr(ndisc, neigh_eth_addr);
> +                       ip6_send_na(neigh_eth_addr, &ip6->saddr,
> +                                   &ndisc->target);
> +               }
> +               break;
> +
> +       case IPV6_NDISC_NEIGHBOUR_ADVERTISEMENT:
> +               /* are we waiting for a reply ? */
> +               if (ip6_is_unspecified_addr(&net_nd_sol_packet_ip6))
> +                       break;
> +
> +               if ((memcmp(&ndisc->target, &net_nd_rep_packet_ip6,
> +                           sizeof(struct in6_addr)) == 0) &&
> +                   ip6_ndisc_has_option(ip6, ND_OPT_TARGET_LL_ADDR)) {
> +                       ip6_ndisc_extract_enetaddr(ndisc, neigh_eth_addr);
> +
> +                       /* save address for later use */
> +                       if (net_nd_packet_mac != NULL)
> +                               memcpy(net_nd_packet_mac, neigh_eth_addr, 6);
> +
> +                       /* modify header, and transmit it */
> +                       memcpy(((struct ethernet_hdr *)net_nd_tx_packet)->et_dest,
> +                              neigh_eth_addr, 6);
> +                       net_send_packet(net_nd_tx_packet,
> +                                       net_nd_tx_packet_size);
> +
> +                       /* no ND request pending now */
> +                       net_nd_sol_packet_ip6 = net_null_addr_ip6;
> +                       net_nd_tx_packet_size = 0;
> +                       net_nd_packet_mac = NULL;
> +               }
> +               break;
> +       default:
> +               debug("Unexpected ICMPv6 type 0x%x\n", icmp->icmp6_type);
> +       }
> +}
> diff --git a/net/ndisc.h b/net/ndisc.h
> new file mode 100644
> index 0000000..7ade0fc
> --- /dev/null
> +++ b/net/ndisc.h
> @@ -0,0 +1,27 @@
> +/*
> + * net/ndisc.h
> + *
> + * (C) Copyright 2013 Allied Telesis Labs NZ
> + *
> + * SPDX-License-Identifier:    GPL-2.0+
> + */
> +
> +/* IPv6 destination address of packet waiting for ND */
> +extern struct in6_addr net_nd_sol_packet_ip6;
> +/* IPv6 address we are expecting ND advert from */
> +extern struct in6_addr net_nd_rep_packet_ip6;

This is not used outside of ndisc.c so remove it from this header.

The same goes for any others in here that are not needed outside.

> +/* MAC destination address of packet waiting for ND */
> +extern uchar *net_nd_packet_mac;
> +/* pointer to packet waiting to be transmitted after ND is resolved */
> +extern uchar *net_nd_tx_packet;
> +extern uchar net_nd_packet_buf[PKTSIZE_ALIGN + PKTALIGN];
> +/* size of packet waiting to be transmitted */
> +extern int net_nd_tx_packet_size;
> +/* the timer for ND resolution */
> +extern ulong net_nd_timer_start;
> +/* the number of requests we have sent so far */
> +extern int net_nd_try;
> +
> +void ndisc_receive(struct ethernet_hdr *et, struct ip6_hdr *ip6, int len);
> +void ip6_NDISC_Request(void);
> +void ip6_NDISC_TimeoutCheck(void);
> diff --git a/net/net.c b/net/net.c
> index a115ce2..349a18e 100644
> --- a/net/net.c
> +++ b/net/net.c
> @@ -86,6 +86,7 @@
>  #include <environment.h>
>  #include <errno.h>
>  #include <net.h>
> +#include <net6.h>
>  #include <net/tftp.h>
>  #if defined(CONFIG_STATUS_LED)
>  #include <miiphy.h>
> @@ -94,6 +95,7 @@
>  #include <watchdog.h>
>  #include <linux/compiler.h>
>  #include "arp.h"
> +#include "ndisc.h"
>  #include "bootp.h"
>  #include "cdp.h"
>  #if defined(CONFIG_CMD_DNS)
> @@ -311,6 +313,7 @@ static int on_dnsip(const char *name, const char *value, enum env_op op,
>  U_BOOT_ENV_CALLBACK(dnsip, on_dnsip);
>  #endif
>
> +

No need to add white space here.

>  /*
>   * Check if autoload is enabled. If so, use either NFS or TFTP to download
>   * the boot file.
> @@ -341,8 +344,12 @@ void net_auto_load(void)
>
>  static void net_init_loop(void)
>  {
> -       if (eth_get_dev())
> +       if (eth_get_dev()) {
>                 memcpy(net_ethaddr, eth_get_ethaddr(), 6);
> +#ifdef CONFIG_CMD_NET6
> +               ip6_make_lladdr(&net_link_local_ip6, net_ethaddr);
> +#endif
> +       }
>
>         return;
>  }
> @@ -376,6 +383,9 @@ void net_init(void)
>                                 (i + 1) * PKTSIZE_ALIGN;
>                 }
>                 arp_init();
> +#ifdef CONFIG_CMD_NET6
> +               ip6_NDISC_init();
> +#endif
>                 net_clear_handlers();
>
>                 /* Only need to setup buffer pointers once. */
> @@ -478,6 +488,11 @@ restart:
>                         ping_start();
>                         break;
>  #endif
> +#ifdef CONFIG_CMD_PING6
> +               case PING6:
> +                       ping6_start();
> +                       break;
> +#endif
>  #if defined(CONFIG_CMD_NFS)
>                 case NFS:
>                         nfs_start();
> @@ -555,6 +570,9 @@ restart:
>                 if (ctrlc()) {
>                         /* cancel any ARP that may not have completed */
>                         net_arp_wait_packet_ip.s_addr = 0;
> +#ifdef CONFIG_CMD_NET6
> +                       net_nd_sol_packet_ip6 = net_null_addr_ip6;
> +#endif
>
>                         net_cleanup_loop();
>                         eth_halt();
> @@ -570,6 +588,9 @@ restart:
>                 }
>
>                 arp_timeout_check();
> +#ifdef CONFIG_CMD_NET6
> +               ip6_NDISC_TimeoutCheck();
> +#endif
>
>                 /*
>                  *      Check for a timeout, and run the timeout handler
> @@ -1141,6 +1162,11 @@ void net_process_received_packet(uchar *in_packet, int len)
>                 rarp_receive(ip, len);
>                 break;
>  #endif
> +#ifdef CONFIG_CMD_NET6
> +       case PROT_IP6:
> +               net_ip6_handler(et, (struct ip6_hdr *)ip, len);
> +               break;
> +#endif
>         case PROT_IP:
>                 debug_cond(DEBUG_NET_PKT, "Got IP\n");
>                 /* Before we start poking the header, make sure it is there */
> @@ -1295,6 +1321,14 @@ static int net_check_prereq(enum proto_t protocol)
>                 }
>                 goto common;
>  #endif
> +#ifdef CONFIG_CMD_PING6
> +       case PING6:
> +               if (ip6_is_unspecified_addr(&net_ping_ip6)) {
> +                       puts("*** ERROR: ping address not given\n");
> +                       return 1;
> +               }
> +               goto common;
> +#endif

All the ping stuff seems like it could come out into a separate patch.

>  #if defined(CONFIG_CMD_SNTP)
>         case SNTP:
>                 if (net_ntp_server.s_addr == 0) {
> diff --git a/net/net6.c b/net/net6.c
> new file mode 100644
> index 0000000..2315704
> --- /dev/null
> +++ b/net/net6.c
> @@ -0,0 +1,375 @@
> +/*
> + * Simple IPv6 network layer implementation.
> + *
> + * Based and/or adapted from the IPv4 network layer in net.[hc]
> + *
> + * (C) Copyright 2013 Allied Telesis Labs NZ
> + *
> + * SPDX-License-Identifier:    GPL-2.0+
> + */
> +
> +/*
> + * General Desription:
> + *
> + * The user interface supports commands for TFTP6.
> + * Also, we support Neighbour discovery internally. Depending on available
> + * data, these interact as follows:
> + *
> + * Neighbour Discovery:
> + *
> + *      Prerequisites:  - own ethernet address
> + *                      - own IPv6 address
> + *                      - TFTP server IPv6 address
> + *      We want:        - TFTP server ethernet address
> + *      Next step:      TFTP
> + *
> + * TFTP over IPv6:
> + *
> + *      Prerequisites:  - own ethernet address
> + *                      - own IPv6 address
> + *                      - TFTP server IPv6 address
> + *                      - TFTP server ethernet address
> + *                      - name of bootfile (if unknown, we use a default name
> + *                        derived from our own IPv6 address)
> + *      We want:        - load the boot file
> + *      Next step:      none
> + *
> + */
> +#define DEBUG
> +#include <common.h>
> +#include <environment.h>
> +#include <net.h>
> +#include <net6.h>
> +#include "ndisc.h"
> +
> +/* NULL IPv6 address */
> +struct in6_addr const net_null_addr_ip6 = ZERO_IPV6_ADDR;
> +/* Our gateway's IPv6 address */
> +struct in6_addr net_gateway6 = ZERO_IPV6_ADDR;
> +/* Our IPv6 addr (0 = unknown) */
> +struct in6_addr net_ip6 = ZERO_IPV6_ADDR;
> +/* Our link local IPv6 addr (0 = unknown) */
> +struct in6_addr net_link_local_ip6 = ZERO_IPV6_ADDR;
> +/* set server IPv6 addr (0 = unknown) */
> +struct in6_addr net_server_ip6 = ZERO_IPV6_ADDR;
> +/* The prefix length of our network */
> +u_int32_t net_prefix_length;
> +
> +static int on_ip6addr(const char *name, const char *value, enum env_op op,
> +                     int flags)
> +{
> +       if (flags & H_PROGRAMMATIC)
> +               return 0;
> +
> +       return string_to_ip6(value, &net_ip6);
> +}
> +
> +U_BOOT_ENV_CALLBACK(ip6addr, on_ip6addr);
> +
> +static int on_gatewayip6(const char *name, const char *value, enum env_op op,
> +                        int flags)
> +{
> +       if (flags & H_PROGRAMMATIC)
> +               return 0;
> +
> +       return string_to_ip6(value, &net_gateway6);
> +}
> +
> +U_BOOT_ENV_CALLBACK(gatewayip6, on_gatewayip6);
> +
> +static int on_prefixlength6(const char *name, const char *value, enum env_op op,
> +                           int flags)
> +{
> +       if (flags & H_PROGRAMMATIC)
> +               return 0;
> +
> +       net_prefix_length = simple_strtoul(value, NULL, 10);
> +
> +       return 0;
> +}
> +
> +U_BOOT_ENV_CALLBACK(prefixlength6, on_prefixlength6);
> +
> +static int on_serverip6(const char *name, const char *value, enum env_op op,
> +                       int flags)
> +{
> +       if (flags & H_PROGRAMMATIC)
> +               return 0;
> +
> +       return string_to_ip6(value, &net_server_ip6);
> +}
> +
> +U_BOOT_ENV_CALLBACK(serverip6, on_serverip6);
> +
> +int ip6_is_unspecified_addr(struct in6_addr *addr)
> +{
> +       return (addr->s6_addr32[0] | addr->s6_addr32[1] |
> +               addr->s6_addr32[2] | addr->s6_addr32[3]) == 0;
> +}
> +
> +/**
> + * We have 2 addresses that we should respond to. A link
> + * local address and a global address. This returns true
> + * if the specified address matches either of these.
> + */
> +int ip6_is_our_addr(struct in6_addr *addr)
> +{
> +       return memcmp(addr, &net_link_local_ip6, sizeof(struct in6_addr)) == 0 ||
> +              memcmp(addr, &net_ip6, sizeof(struct in6_addr)) == 0;
> +}
> +
> +void ip6_make_eui(unsigned char eui[8], unsigned char const enetaddr[6])
> +{
> +       memcpy(eui, enetaddr, 3);
> +       memcpy(&eui[5], &enetaddr[3], 3);
> +       eui[3] = 0xFF;
> +       eui[4] = 0xFE;
> +       eui[0] ^= 2;            /* "u" bit set to indicate global scope */
> +}
> +
> +void ip6_make_lladdr(struct in6_addr *lladr, unsigned char const enetaddr[6])
> +{
> +       uchar eui[8];
> +
> +       memset(lladr, 0, sizeof(struct in6_addr));
> +       lladr->s6_addr16[0] = htons(IPV6_LINK_LOCAL_PREFIX);
> +       ip6_make_eui(eui, enetaddr);
> +       memcpy(&lladr->s6_addr[8], eui, 8);
> +}
> +
> +/*
> + * Given an IPv6 address generate an equivalent Solicited Node Multicast
> + * Address (SNMA) as described in RFC2461.
> + */
> +void ip6_make_SNMA(struct in6_addr *mcast_addr, struct in6_addr *ip6_addr)

Lowercase.

> +{
> +       memset(mcast_addr, 0, sizeof(struct in6_addr));
> +       mcast_addr->s6_addr[0] = 0xff;
> +       mcast_addr->s6_addr[1] = IPV6_ADDRSCOPE_LINK;
> +       mcast_addr->s6_addr[11] = 0x01;
> +       mcast_addr->s6_addr[12] = 0xff;
> +       mcast_addr->s6_addr[13] = ip6_addr->s6_addr[13];
> +       mcast_addr->s6_addr[14] = ip6_addr->s6_addr[14];
> +       mcast_addr->s6_addr[15] = ip6_addr->s6_addr[15];
> +}
> +
> +/*
> + * Given an IPv6 address generate the multicast MAC address that corresponds to
> + * it.
> + */
> +void
> +ip6_make_mult_ethdstaddr(unsigned char enetaddr[6], struct in6_addr *mcast_addr)
> +{
> +       enetaddr[0] = 0x33;
> +       enetaddr[1] = 0x33;
> +       memcpy(&enetaddr[2], &mcast_addr->s6_addr[12], 4);
> +}
> +
> +int
> +ip6_addr_in_subnet(struct in6_addr *our_addr, struct in6_addr *neigh_addr,
> +                  __u32 plen)
> +{
> +       __be32 *addr_dwords;
> +       __be32 *neigh_dwords;
> +
> +       addr_dwords = our_addr->s6_addr32;
> +       neigh_dwords = neigh_addr->s6_addr32;
> +
> +       while (plen > 32) {
> +               if (*addr_dwords++ != *neigh_dwords++)
> +                       return 0;
> +
> +               plen -= 32;
> +       }
> +
> +       /* Check any remaining bits. */
> +       if (plen > 0) {
> +               /* parameters are in network byte order.
> +                  Does this work on a LE host? */

So is this still an outstanding question? Probably worth testing on a
LE target, but it should work I believe.

> +               if ((*addr_dwords >> (32 - plen)) !=
> +                   (*neigh_dwords >> (32 - plen))) {
> +                       return 0;
> +               }
> +       }
> +
> +       return 1;
> +}
> +
> +static inline unsigned int csum_fold(unsigned int sum)
> +{
> +       sum = (sum & 0xffff) + (sum >> 16);
> +       sum = (sum & 0xffff) + (sum >> 16);
> +
> +       return ~sum;
> +}
> +
> +static __u32 csum_do_csum(const __u8 *buff, int len)
> +{
> +       int odd, count;
> +       unsigned long result = 0;
> +
> +       if (len <= 0)
> +               goto out;
> +       odd = 1 & (unsigned long)buff;
> +       if (odd) {
> +               result = *buff;
> +               len--;
> +               buff++;
> +       }
> +       count = len >> 1;       /* nr of 16-bit words.. */
> +       if (count) {
> +               if (2 & (unsigned long)buff) {
> +                       result += *(unsigned short *)buff;
> +                       count--;
> +                       len -= 2;
> +                       buff += 2;
> +               }
> +               count >>= 1;    /* nr of 32-bit words.. */
> +               if (count) {
> +                       unsigned long carry = 0;
> +                       do {
> +                               unsigned long w = *(unsigned long *)buff;
> +                               count--;
> +                               buff += 4;
> +                               result += carry;
> +                               result += w;
> +                               carry = (w > result);
> +                       } while (count);
> +                       result += carry;
> +                       result = (result & 0xffff) + (result >> 16);
> +               }
> +               if (len & 2) {
> +                       result += *(unsigned short *)buff;
> +                       buff += 2;
> +               }
> +       }
> +       if (len & 1)
> +               result += (*buff << 8);
> +       result = ~csum_fold(result);
> +       if (odd)
> +               result = ((result >> 8) & 0xff) | ((result & 0xff) << 8);
> +out:
> +       return result;
> +}
> +
> +unsigned int csum_partial(const unsigned char *buff, int len, unsigned int sum)
> +{
> +       unsigned int result = csum_do_csum(buff, len);
> +
> +       /* add in old sum, and carry.. */
> +       result += sum;
> +       /* 16+c bits -> 16 bits */
> +       result = (result & 0xffff) + (result >> 16);
> +       return result;
> +}
> +
> +unsigned short int
> +csum_ipv6_magic(struct in6_addr *saddr, struct in6_addr *daddr,
> +               __u16 len, unsigned short proto, unsigned int csum)
> +{

Please add some comments to this to make it less magic. :)

> +       int i;
> +       int carry;
> +       __u32 ulen;
> +       __u32 uproto;
> +       unsigned int finalsum;
> +
> +       for (i = 0; i < 4; i++) {
> +               csum += saddr->s6_addr32[i];
> +               carry = (csum < saddr->s6_addr32[i]);
> +               csum += carry;
> +
> +               csum += daddr->s6_addr32[i];
> +               carry = (csum < daddr->s6_addr32[i]);
> +               csum += carry;
> +       }
> +
> +       ulen = htonl((__u32)len);
> +       csum += ulen;
> +       carry = (csum < ulen);
> +       csum += carry;
> +
> +       uproto = htonl(proto);
> +       csum += uproto;
> +       carry = (csum < uproto);
> +       csum += carry;
> +
> +       finalsum = csum_fold(csum);
> +       if ((finalsum & 0xffff) == 0x0000)
> +               return 0xffff;
> +       else if ((finalsum & 0xffff) == 0xffff)
> +               return 0x0000;
> +       else
> +               return finalsum;
> +}
> +
> +int
> +ip6_add_hdr(uchar *xip, struct in6_addr *src, struct in6_addr *dest,
> +           int nextheader, int hoplimit, int payload_len)
> +{
> +       struct ip6_hdr *ip6 = (struct ip6_hdr *)xip;
> +
> +       ip6->version = 6;
> +       ip6->priority = 0;
> +       ip6->flow_lbl[0] = 0;
> +       ip6->flow_lbl[1] = 0;
> +       ip6->flow_lbl[2] = 0;
> +       ip6->payload_len = htons(payload_len);
> +       ip6->nexthdr = nextheader;
> +       ip6->hop_limit = hoplimit;
> +       net_copy_ip6(&ip6->saddr, src);
> +       net_copy_ip6(&ip6->daddr, dest);
> +
> +       return sizeof(struct ip6_hdr);
> +}
> +
> +void net_ip6_handler(struct ethernet_hdr *et, struct ip6_hdr *ip6, int len)
> +{
> +       struct in_addr zero_ip = {.s_addr = 0 };
> +       struct icmp6hdr *icmp;
> +       struct udp_hdr *udp;
> +       __u16 csum;
> +       __u16 hlen;
> +
> +       if (len < IP6_HDR_SIZE)
> +               return;
> +
> +       if (ip6->version != 6)
> +               return;
> +
> +       switch (ip6->nexthdr) {
> +       case IPPROTO_ICMPV6:
> +               icmp = (struct icmp6hdr *)(((uchar *)ip6) + IP6_HDR_SIZE);
> +               csum = icmp->icmp6_cksum;
> +               hlen = ntohs(ip6->payload_len);
> +               icmp->icmp6_cksum = 0;
> +               /* checksum */
> +               icmp->icmp6_cksum = csum_ipv6_magic(&ip6->saddr, &ip6->daddr,
> +                                                   hlen, IPPROTO_ICMPV6,
> +                                                   csum_partial((__u8 *)icmp, hlen, 0));
> +               if (icmp->icmp6_cksum != csum)
> +                       return;
> +
> +               switch (icmp->icmp6_type) {
> +#ifdef CONFIG_CMD_PING6
> +               case IPV6_ICMP_ECHO_REQUEST:
> +               case IPV6_ICMP_ECHO_REPLY:
> +                       ping6_receive(et, ip6, len);
> +                       break;
> +#endif /* CONFIG_CMD_PING6 */
> +
> +               case IPV6_NDISC_NEIGHBOUR_SOLICITATION:
> +               case IPV6_NDISC_NEIGHBOUR_ADVERTISEMENT:
> +                       ndisc_receive(et, ip6, len);
> +                       break;
> +
> +               default:
> +                       return;
> +                       break;
> +               }
> +               break;
> +
> +       default:
> +               return;
> +               break;
> +       }
> +}
> diff --git a/net/ping6.c b/net/ping6.c
> new file mode 100644
> index 0000000..aa93dfa
> --- /dev/null
> +++ b/net/ping6.c
> @@ -0,0 +1,111 @@
> +/*
> + * net/ping6.c
> + *
> + * (C) Copyright 2013 Allied Telesis Labs NZ
> + *
> + * SPDX-License-Identifier:    GPL-2.0+
> + */
> +#define DEBUG
> +#include <common.h>
> +#include <net.h>
> +#include <net6.h>
> +#include "ndisc.h"
> +
> +static ushort seq_no;
> +
> +/* the ipv6 address to ping */
> +struct in6_addr net_ping_ip6;
> +
> +int
> +ip6_make_ping(uchar *eth_dst_addr, struct in6_addr *neigh_addr, uchar *pkt)
> +{
> +       struct echo_msg *msg;
> +       __u16 len;
> +       uchar *pkt_old = pkt;
> +
> +       len = sizeof(struct echo_msg);
> +
> +       pkt += net_set_ether(pkt, eth_dst_addr, PROT_IP6);
> +       pkt += ip6_add_hdr(pkt, &net_ip6, neigh_addr, IPPROTO_ICMPV6,
> +                          IPV6_NDISC_HOPLIMIT, len);
> +
> +       /* ICMPv6 - Echo */
> +       msg = (struct echo_msg *)pkt;
> +       msg->icmph.icmp6_type = IPV6_ICMP_ECHO_REQUEST;
> +       msg->icmph.icmp6_code = 0;
> +       msg->icmph.icmp6_cksum = 0;
> +       msg->icmph.icmp6_identifier = 0;
> +       msg->icmph.icmp6_sequence = htons(seq_no++);
> +       msg->id = msg->icmph.icmp6_identifier;  /* these seem redundant */
> +       msg->sequence = msg->icmph.icmp6_sequence;
> +
> +       /* checksum */
> +       msg->icmph.icmp6_cksum = csum_ipv6_magic(&net_ip6, neigh_addr, len,
> +                                                IPPROTO_ICMPV6,
> +                                                csum_partial((__u8 *)msg, len, 0));
> +
> +       pkt += len;
> +
> +       return pkt - pkt_old;
> +}
> +
> +int ping6_send(void)
> +{
> +       uchar *pkt;
> +       static uchar mac[6];
> +
> +       /* always send neighbor solicit */
> +
> +       memcpy(mac, net_null_ethaddr, 6);
> +
> +       net_nd_sol_packet_ip6 = net_ping_ip6;
> +       net_nd_packet_mac = mac;
> +
> +       pkt = net_nd_tx_packet;
> +       pkt += ip6_make_ping(mac, &net_ping_ip6, pkt);
> +
> +       /* size of the waiting packet */
> +       net_nd_tx_packet_size = (pkt - net_nd_tx_packet);
> +
> +       /* and do the ARP request */
> +       net_nd_try = 1;
> +       net_nd_timer_start = get_timer(0);
> +       ip6_NDISC_Request();
> +       return 1;               /* waiting */
> +}
> +
> +static void ping6_timeout(void)
> +{
> +       eth_halt();
> +       net_set_state(NETLOOP_FAIL);    /* we did not get the reply */
> +}
> +
> +void ping6_start(void)
> +{
> +       printf("Using %s device\n", eth_get_name());
> +       net_set_timeout_handler(10000UL, ping6_timeout);
> +
> +       ping6_send();
> +}
> +
> +void ping6_receive(struct ethernet_hdr *et, struct ip6_hdr *ip6, int len)
> +{
> +       struct icmp6hdr *icmp =
> +           (struct icmp6hdr *)(((uchar *)ip6) + IP6_HDR_SIZE);
> +       struct in6_addr src_ip;
> +
> +       switch (icmp->icmp6_type) {
> +       case IPV6_ICMP_ECHO_REPLY:
> +               src_ip = ip6->saddr;
> +               if (memcmp(&net_ping_ip6, &src_ip, sizeof(struct in6_addr)) != 0)
> +                       return;
> +               net_set_state(NETLOOP_SUCCESS);
> +               break;
> +       case IPV6_ICMP_ECHO_REQUEST:
> +               debug("Got ICMPv6 ECHO REQUEST from %pI6c\n", &ip6->saddr);
> +               /* ignore for now.... */
> +               break;
> +       default:
> +               debug("Unexpected ICMPv6 type 0x%x\n", icmp->icmp6_type);
> +       }
> +}
> --
> 2.5.3
>
>
> _______________________________________________
> U-Boot mailing list
> U-Boot at lists.denx.de
> http://lists.denx.de/mailman/listinfo/u-boot
>

^ permalink raw reply	[flat|nested] 31+ messages in thread

* [U-Boot] [RFC PATCH 6/8] net: TFTP over IPv6
  2015-10-12  7:43 ` [U-Boot] [RFC PATCH 6/8] net: TFTP over IPv6 Chris Packham
@ 2015-11-02 20:43   ` Joe Hershberger
  2015-11-03 10:15     ` Chris Packham
  2015-11-03 10:41     ` Jean-Pierre Tosoni
  0 siblings, 2 replies; 31+ messages in thread
From: Joe Hershberger @ 2015-11-02 20:43 UTC (permalink / raw)
  To: u-boot

Hi Chris,

On Mon, Oct 12, 2015 at 2:43 AM, Chris Packham <judge.packham@gmail.com> wrote:
> Add support for UDP/TFTP over IPv6.
>
> Signed-off-by: Chris Packham <judge.packham@gmail.com>
> ---
> One problem with the [hostIpAddr:]fileName syntax is that IPv6 addresses
> contains colons. So tftp_start() would be confused by 'tftpboot6
> $loadaddr 2001:db8::1:zImage'. It is probably possible to change the
> parsing to separate the host from the filename by parsing from the end
> (i.e. use strrchr() instead of strchr()) but then there are error cases
> that may not be handled correctly (e.g. omitting the filename).

I think we should just change the filename separator for tftp6. How about ','?

>  common/cmd_net.c | 13 ++++++++++++
>  include/net6.h   |  4 ++++
>  net/net.c        |  3 +++
>  net/net6.c       | 64 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++
>  net/tftp.c       | 37 ++++++++++++++++++++++++++++++++
>  5 files changed, 121 insertions(+)
>
> diff --git a/common/cmd_net.c b/common/cmd_net.c
> index 271f91d..3541599 100644
> --- a/common/cmd_net.c
> +++ b/common/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] [bootfilename]"
> +);
> +#endif
> +
>  #ifdef CONFIG_CMD_TFTPPUT
>  int do_tftpput(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])
>  {
> diff --git a/include/net6.h b/include/net6.h
> index a0374df..df6d38e 100644
> --- a/include/net6.h
> +++ b/include/net6.h
> @@ -264,6 +264,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 349a18e..9e682b4 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 2315704..f5e272a 100644
> --- a/net/net6.c
> +++ b/net/net6.c
> @@ -322,6 +322,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 + IP6_UDPHDR_SIZE);
> +       /* checksum */
> +       udp->udp_xsum = 0;
> +       udp->udp_xsum = csum_ipv6_magic(&net_ip6, dest, len + IP6_UDPHDR_SIZE,
> +               IPPROTO_UDP, csum_partial((__u8 *)udp, len + IP6_UDPHDR_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 + IP6_UDPHDR_SIZE);
> +               memcpy(pkt, (uchar *)udp, len + IP6_UDPHDR_SIZE);
> +
> +               /* size of the waiting packet */
> +               net_nd_tx_packet_size = (pkt - net_nd_tx_packet) + IP6_UDPHDR_SIZE + len;
> +
> +               /* and do the neighbor solicitation */
> +               net_nd_try = 1;
> +               net_nd_timer_start = get_timer(0);
> +               ip6_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 + IP6_UDPHDR_SIZE);
> +       (void) eth_send(net_tx_packet, (pkt - net_tx_packet) + IP6_UDPHDR_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 };
> @@ -368,6 +412,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 +
> +                                       IP6_UDPHDR_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 1a51131..1463bf2 100644
> --- a/net/tftp.c
> +++ b/net/tftp.c
> @@ -10,6 +10,7 @@
>  #include <command.h>
>  #include <mapmem.h>
>  #include <net.h>
> +#include <net6.h>
>  #include <net/tftp.h>
>  #include "bootp.h"
>  #ifdef CONFIG_SYS_DIRECT_FLASH_TFTP
> @@ -94,6 +95,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
> @@ -129,6 +134,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;
> @@ -341,6 +348,11 @@ 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 + IP6_UDPHDR_SIZE;
> +       else
> +#endif
>         pkt = net_tx_packet + net_eth_hdr_size() + IP_UDP_HDR_SIZE;
>
>         switch (tftp_state) {
> @@ -440,6 +452,12 @@ static void tftp_send(void)
>                 break;
>         }
>
> +#ifdef CONFIG_CMD_NET6
> +       if (tftp6_active)
> +               net_send_udp_packet6(net_server_ethaddr, &net_server_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);
>  }
> @@ -747,6 +765,16 @@ void tftp_start(enum proto_t protocol)
>         }
>
>         printf("Using %s device\n", eth_get_name());
> +#ifdef CONFIG_CMD_NET6
> +       tftp6_active = (protocol == TFTP6);
> +       if (tftp6_active) {
> +               printf("TFTP from server %pI6c; our IP address is %pI6c",
> +                      &net_server_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",
> @@ -756,6 +784,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, &net_server_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.5.3
>
> _______________________________________________
> U-Boot mailing list
> U-Boot at lists.denx.de
> http://lists.denx.de/mailman/listinfo/u-boot

^ permalink raw reply	[flat|nested] 31+ messages in thread

* [U-Boot] [RFC PATCH 7/8] net: IPv6 documentation
  2015-10-12  7:43 ` [U-Boot] [RFC PATCH 7/8] net: IPv6 documentation Chris Packham
@ 2015-11-02 20:43   ` Joe Hershberger
  0 siblings, 0 replies; 31+ messages in thread
From: Joe Hershberger @ 2015-11-02 20:43 UTC (permalink / raw)
  To: u-boot

Hi Chris,

On Mon, Oct 12, 2015 at 2:43 AM, Chris Packham <judge.packham@gmail.com> wrote:
> Signed-off-by: Chris Packham <judge.packham@gmail.com>
> ---

Acked-by: Joe Hershberger <joe.hershberger@ni.com>

^ permalink raw reply	[flat|nested] 31+ messages in thread

* [U-Boot] [RFC PATCH 8/8] net: e1000 enable multicast reception
  2015-10-12  7:43 ` [U-Boot] [RFC PATCH 8/8] net: e1000 enable multicast reception Chris Packham
@ 2015-11-02 20:44   ` Joe Hershberger
  0 siblings, 0 replies; 31+ messages in thread
From: Joe Hershberger @ 2015-11-02 20:44 UTC (permalink / raw)
  To: u-boot

Hi Chris,

On Mon, Oct 12, 2015 at 2:43 AM, Chris Packham <judge.packham@gmail.com> wrote:
> IPv6 neighbor discovery uses various multicast addresses to send the
> request and receive the response. For neighbor discovery to work
> properly in U-boot the Ethernet device needs to support joining/leaving
> various L2 multicast groups or it needs to support multicast/promiscuous
> mode. For the sake of simplicity the latter approach has been taken. The
> e1000 hardware has slightly finer grained control in that it is possible
> to enable support for multicast-promiscuous mode separately from unicast
> so the extra traffic received is less.
>
> Signed-off-by: Chris Packham <judge.packham@gmail.com>

Acked-by: Joe Hershberger <joe.hershberger@ni.com>

^ permalink raw reply	[flat|nested] 31+ messages in thread

* [U-Boot] [RFC PATCH 5/8] net: ipv6 support
  2015-11-02 20:43   ` Joe Hershberger
@ 2015-11-03 10:11     ` Chris Packham
  2015-11-03 19:41       ` Joe Hershberger
  0 siblings, 1 reply; 31+ messages in thread
From: Chris Packham @ 2015-11-03 10:11 UTC (permalink / raw)
  To: u-boot

Hi Joe,

I've answered a few questions below. I'll address your comments more
completely before sending another round next week (or I can sit on it
for longer if you want me to give you some breathing room).

On Tue, Nov 3, 2015 at 9:43 AM, Joe Hershberger
<joe.hershberger@gmail.com> wrote:
> Hi Chris,
>
> On Mon, Oct 12, 2015 at 2:43 AM, Chris Packham <judge.packham@gmail.com> wrote:
>> Adds basic support for IPv6. Neighbor discovery and ping6 are the only
>> things supported at the moment.
>>
>> Helped-by: Hanna Hawa <hannah@marvell.com> [endian & alignment fixes]
>> Signed-off-by: Chris Packham <judge.packham@gmail.com>
>> ---
>> Now we have something functional. With this you can do something like
>> 'setenv ipaddr6 3ffe::2' and 'ping6 3ffe::1' should work.
>>
>> I seem to have a problem that when you send a ping6 for a non-existent
>> address that ends up stuck and the next non-ipv6 net operation tries to
>> resolve it. I suspect this is because the pending neighbor discovery
>> information isn't cleaned up properly, I need to look into that.
>>
>> The environment variable prefixlength6 is a bit fiddly. No-one uses a
>> netmask6 (it'd mean a lot of ffff:ffff:...) it's almost always done by
>> including the prefix length in the address (e.g. ip addr add
>> 2001:db8::1/64) I'm contemplating adopting that syntax and dropping
>> prefixlength6.
>>
>> This patch is bigger than I'd like it to be but I'm not sure how to
>> split it up and keep the parts build able.
>
> It is pretty huge. It's taken me a while to get through it.
>

Thanks for making the effort to review. I realise it's pretty huge and
I'll put some effort into splitting it futher. One obvious thing that
could be split out is the new environment variables.

>>  common/Kconfig         |  15 ++
>>  common/cmd_net.c       |  28 ++++
>>  include/env_callback.h |   9 ++
>>  include/env_flags.h    |  10 ++
>>  include/net.h          |   5 +-
>>  include/net6.h         | 212 ++++++++++++++++++++++++++++
>>  net/Kconfig            |   5 +
>>  net/Makefile           |   3 +
>>  net/ndisc.c            | 269 +++++++++++++++++++++++++++++++++++
>>  net/ndisc.h            |  27 ++++
>>  net/net.c              |  36 ++++-
>>  net/net6.c             | 375 +++++++++++++++++++++++++++++++++++++++++++++++++
>>  net/ping6.c            | 111 +++++++++++++++
>>  13 files changed, 1102 insertions(+), 3 deletions(-)
>>  create mode 100644 net/ndisc.c
>>  create mode 100644 net/ndisc.h
>>  create mode 100644 net/net6.c
>>  create mode 100644 net/ping6.c
>>
>> diff --git a/common/Kconfig b/common/Kconfig
>> index 2c42b8e..c72563d 100644
>> --- a/common/Kconfig
>> +++ b/common/Kconfig
>> @@ -389,6 +389,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
>
> This is added in the next patch, so should probably move there.
>

Will do.

>> +
>>  config CMD_TFTPPUT
>>         bool "tftp put"
>>         help
>> @@ -420,6 +429,12 @@ config CMD_PING
>>         help
>>           Send ICMP ECHO_REQUEST to network host
>>
>> +config CMD_PING6
>> +       bool "ping6"
>> +       depends on CMD_NET6
>> +       help
>> +         Send ICMPv6 ECHO_REQUEST to network host
>
> What makes ping inseparable from the core support?
>

Mainly testing, I can't test the core code without ping. But I can see
that from a patch submission point of few splitting it out will make
review easier.

>> +
>>  config CMD_CDP
>>         bool "cdp"
>>         help
>> diff --git a/common/cmd_net.c b/common/cmd_net.c
>> index b2f3c7b..271f91d 100644
>> --- a/common/cmd_net.c
>> +++ b/common/cmd_net.c
>> @@ -11,6 +11,7 @@
>>  #include <common.h>
>>  #include <command.h>
>>  #include <net.h>
>> +#include <net6.h>
>>
>>  static int netboot_common(enum proto_t, cmd_tbl_t *, int, char * const []);
>>
>> @@ -284,6 +285,33 @@ U_BOOT_CMD(
>>  );
>>  #endif
>>
>> +#ifdef CONFIG_CMD_PING6
>> +int do_ping6(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])
>> +{
>> +       if (argc < 2)
>> +               return -1;
>> +
>> +       if (string_to_ip6(argv[1], &net_ping_ip6) != 0)
>> +               return CMD_RET_USAGE;
>> +
>> +       if (net_loop(PING6) < 0) {
>> +               printf("ping6 failed; host %pI6c is not alive\n",
>> +                      &net_ping_ip6);
>> +               return 1;
>> +       }
>> +
>> +       printf("host %pI6c is alive\n", &net_ping_ip6);
>> +
>> +       return 0;
>> +}
>> +
>> +U_BOOT_CMD(
>> +       ping6,  2,      1,      do_ping6,
>> +       "send ICMPv6 ECHO_REQUEST to network host",
>> +       "pingAddress"
>> +);
>> +#endif /* CONFIG_CMD_PING6 */
>> +
>>  #if defined(CONFIG_CMD_CDP)
>>
>>  static void cdp_update_env(void)
>> diff --git a/include/env_callback.h b/include/env_callback.h
>> index 90b95b5..9027f3f 100644
>> --- a/include/env_callback.h
>> +++ b/include/env_callback.h
>> @@ -60,6 +60,14 @@
>>  #define NET_CALLBACKS
>>  #endif
>>
>> +#ifdef CONFIG_NET6
>> +#define NET6_CALLBACKS \
>> +       "ip6addr:ip6addr," \
>> +       "serverip6:serverip6," \
>> +       "prefixlength6:prefixlength6,"
>
> I like the other nomenclature better as well (included in the address).
>

I've actually already implemented the code to include the prefixlength
in the address and it makes things a lot more usable (the parsing code
is a bit more complicated).

>> +#else
>> +#define NET6_CALLBACKS
>> +#endif
>>  /*
>>   * This list of callback bindings is static, but may be overridden by defining
>>   * a new association in the ".callbacks" environment variable.
>> @@ -68,6 +76,7 @@
>>         ENV_DOT_ESCAPE ENV_FLAGS_VAR ":flags," \
>>         "baudrate:baudrate," \
>>         NET_CALLBACKS \
>> +       NET6_CALLBACKS \
>>         "loadaddr:loadaddr," \
>>         SILENT_CALLBACK \
>>         SPLASHIMAGE_CALLBACK \
>> diff --git a/include/env_flags.h b/include/env_flags.h
>> index 8823fb9..6e1891d 100644
>> --- a/include/env_flags.h
>> +++ b/include/env_flags.h
>> @@ -65,6 +65,15 @@ enum env_flags_varaccess {
>>  #define NET_FLAGS
>>  #endif
>>
>> +#ifdef CONFIG_CMD_NET6
>
> I think CONFIG_NET6 would be better here.
>
>> +#define NET6_FLAGS \
>> +       "ip6addr:s," \
>> +       "serverip6:s," \
>> +       "prefixlength6:d,"
>> +#else
>> +#define NET6_FLAGS
>> +#endif
>> +
>>  #ifndef CONFIG_ENV_OVERWRITE
>>  #define SERIAL_FLAGS "serial#:so,"
>>  #else
>> @@ -74,6 +83,7 @@ enum env_flags_varaccess {
>>  #define ENV_FLAGS_LIST_STATIC \
>>         ETHADDR_FLAGS \
>>         NET_FLAGS \
>> +       NET6_FLAGS \
>>         SERIAL_FLAGS \
>>         CONFIG_ENV_FLAGS_LIST_STATIC
>>
>> diff --git a/include/net.h b/include/net.h
>> index 3a787cc..4f59609 100644
>> --- a/include/net.h
>> +++ b/include/net.h
>> @@ -316,6 +316,7 @@ struct vlan_ethernet_hdr {
>>  #define VLAN_ETHER_HDR_SIZE    (sizeof(struct vlan_ethernet_hdr))
>>
>>  #define PROT_IP                0x0800          /* IP protocol                  */
>> +#define PROT_IP6        0x86DD          /* IPv6 protocol               */
>>  #define PROT_ARP       0x0806          /* IP ARP protocol              */
>>  #define PROT_RARP      0x8035          /* IP ARP protocol              */
>>  #define PROT_VLAN      0x8100          /* IEEE 802.1q protocol         */
>> @@ -512,8 +513,8 @@ extern ushort               net_native_vlan;        /* Our Native VLAN */
>>  extern int             net_restart_wrap;       /* Tried all network devices */
>>
>>  enum proto_t {
>> -       BOOTP, RARP, ARP, TFTPGET, DHCP, PING, DNS, NFS, CDP, NETCONS, SNTP,
>> -       TFTPSRV, TFTPPUT, LINKLOCAL
>> +       BOOTP, RARP, ARP, TFTPGET, DHCP, PING, PING6, DNS, NFS, CDP, NETCONS,
>> +       SNTP, TFTPSRV, TFTPPUT, TFTP6, LINKLOCAL
>
> These should be added with the patch that adds the feature (TFTP6 for
> sure, and probably PING6 when it is a separate patch).
>
>>  };
>>
>>  extern char    net_boot_file_name[128];/* Boot File name */
>> diff --git a/include/net6.h b/include/net6.h
>> index a41eb87..a0374df 100644
>> --- a/include/net6.h
>> +++ b/include/net6.h
>> @@ -22,6 +22,16 @@ struct in6_addr {
>>  #define s6_addr32      in6_u.u6_addr32
>>  };
>>
>> +#define IN6ADDRSZ      sizeof(struct in6_addr)
>> +#define INETHADDRSZ    sizeof(net_ethaddr)
>> +
>> +#define IPV6_ADDRSCOPE_INTF    0x01
>> +#define IPV6_ADDRSCOPE_LINK    0x02
>> +#define IPV6_ADDRSCOPE_AMDIN   0x04
>> +#define IPV6_ADDRSCOPE_SITE    0x05
>> +#define IPV6_ADDRSCOPE_ORG     0x08
>> +#define IPV6_ADDRSCOPE_GLOBAL  0x0E
>> +
>>  /**
>>   * struct ipv6hdr - Internet Protocol V6 (IPv6) header.
>>   *
>> @@ -45,6 +55,154 @@ struct ip6_hdr {
>>         struct in6_addr daddr;
>>  };
>>
>> +#define IP6_HDR_SIZE (sizeof(struct ip6_hdr))
>> +
>> +/* Handy for static initialisations of struct in6_addr, atlhough the
>> + * c99 '= { 0 }' idiom might work depending on you compiler. */
>> +#define ZERO_IPV6_ADDR { { { 0x00, 0x00, 0x00, 0x00, \
>> +                         0x00, 0x00, 0x00, 0x00, \
>> +                         0x00, 0x00, 0x00, 0x00, \
>> +                         0x00, 0x00, 0x00, 0x00 } } }
>> +
>> +#define IPV6_LINK_LOCAL_PREFIX 0xfe80
>> +
>> +struct udp_hdr {
>> +       __be16          udp_src;        /* UDP source port              */
>> +       __be16          udp_dst;        /* UDP destination port         */
>> +       __be16          udp_len;        /* Length of UDP packet         */
>> +       __be16          udp_xsum;       /* Checksum                     */
>> +};
>> +
>> +#define IP6_UDPHDR_SIZE (sizeof(struct udp_hdr))
>
> UDP is shared between IP and IP6, right? There seems to be no reason
> to call it IP6_UDPHDR_SIZE. I would go with UDP_HDR_SIZE, but of
> course that is already defined. Seem like a good candidate to combine
> with the existing implementation.
>
> Maybe that means that struct ip_udp_hdr goes away or maybe it's
> cleaner to keep it as well. Have to dive in to know.
>

I'll take a look. Maybe I can disentangle "ip" from ip_udp_hdr as an
prepatory patch.

>> +enum {
>> +       __ND_OPT_PREFIX_INFO_END        = 0,
>> +       ND_OPT_SOURCE_LL_ADDR           = 1,
>> +       ND_OPT_TARGET_LL_ADDR           = 2,
>> +       ND_OPT_PREFIX_INFO              = 3,
>> +       ND_OPT_REDIRECT_HDR             = 4,
>> +       ND_OPT_MTU                      = 5,
>> +       __ND_OPT_MAX
>> +};
>> +
>> +/* ICMPv6 */
>> +#define IPPROTO_ICMPV6                 58
>> +/* hop limit for neighbour discovery packets */
>> +#define IPV6_NDISC_HOPLIMIT             255
>> +#define NDISC_TIMEOUT                  5000UL
>> +#define NDISC_TIMEOUT_COUNT             3
>> +
>> +struct icmp6hdr {
>> +       __u8    icmp6_type;
>> +#define IPV6_ICMP_ECHO_REQUEST                 128
>> +#define IPV6_ICMP_ECHO_REPLY                   129
>> +#define IPV6_NDISC_ROUTER_SOLICITATION         133
>> +#define IPV6_NDISC_ROUTER_ADVERTISEMENT                134
>> +#define IPV6_NDISC_NEIGHBOUR_SOLICITATION      135
>> +#define IPV6_NDISC_NEIGHBOUR_ADVERTISEMENT     136
>> +#define IPV6_NDISC_REDIRECT                    137
>> +       __u8    icmp6_code;
>> +       __be16  icmp6_cksum;
>> +
>> +       union {
>> +               __be32  un_data32[1];
>> +               __be16  un_data16[2];
>> +               __u8    un_data8[4];
>> +
>> +               struct icmpv6_echo {
>> +                       __be16          identifier;
>> +                       __be16          sequence;
>> +               } u_echo;
>> +
>> +               struct icmpv6_nd_advt {
>> +#if defined(__LITTLE_ENDIAN_BITFIELD)
>> +                       __be32          reserved:5,
>> +                                       override:1,
>> +                                       solicited:1,
>> +                                       router:1,
>> +                                       reserved2:24;
>> +#elif defined(__BIG_ENDIAN_BITFIELD)
>> +                       __be32          router:1,
>> +                                       solicited:1,
>> +                                       override:1,
>> +                                       reserved:29;
>> +#else
>> +#error "Please fix <asm/byteorder.h>"
>> +#endif
>> +               } u_nd_advt;
>> +
>> +               struct icmpv6_nd_ra {
>> +                       __u8            hop_limit;
>> +#if defined(__LITTLE_ENDIAN_BITFIELD)
>> +                       __u8            reserved:6,
>> +                                       other:1,
>> +                                       managed:1;
>> +
>> +#elif defined(__BIG_ENDIAN_BITFIELD)
>> +                       __u8            managed:1,
>> +                                       other:1,
>> +                                       reserved:6;
>> +#else
>> +#error "Please fix <asm/byteorder.h>"
>> +#endif
>> +                       __be16          rt_lifetime;
>> +               } u_nd_ra;
>> +       } icmp6_dataun;
>> +#define icmp6_identifier       icmp6_dataun.u_echo.identifier
>> +#define icmp6_sequence         icmp6_dataun.u_echo.sequence
>> +#define icmp6_pointer          icmp6_dataun.un_data32[0]
>> +#define icmp6_mtu              icmp6_dataun.un_data32[0]
>> +#define icmp6_unused           icmp6_dataun.un_data32[0]
>> +#define icmp6_maxdelay         icmp6_dataun.un_data16[0]
>> +#define icmp6_router           icmp6_dataun.u_nd_advt.router
>> +#define icmp6_solicited                icmp6_dataun.u_nd_advt.solicited
>> +#define icmp6_override         icmp6_dataun.u_nd_advt.override
>> +#define icmp6_ndiscreserved    icmp6_dataun.u_nd_advt.reserved
>> +#define icmp6_hop_limit                icmp6_dataun.u_nd_ra.hop_limit
>> +#define icmp6_addrconf_managed icmp6_dataun.u_nd_ra.managed
>> +#define icmp6_addrconf_other   icmp6_dataun.u_nd_ra.other
>> +#define icmp6_rt_lifetime      icmp6_dataun.u_nd_ra.rt_lifetime
>> +};
>> +
>> +struct nd_msg {
>> +       struct icmp6hdr icmph;
>> +       struct in6_addr target;
>> +       __u8            opt[0];
>> +};
>> +
>> +struct rs_msg {
>> +       struct icmp6hdr icmph;
>> +       __u8            opt[0];
>> +};
>> +
>> +struct ra_msg {
>> +       struct icmp6hdr icmph;
>> +       __u32           reachable_time;
>> +       __u32           retrans_timer;
>> +};
>> +
>> +struct echo_msg {
>> +       struct icmp6hdr icmph;
>> +       __u16           id;
>> +       __u16           sequence;
>> +};
>> +
>> +struct nd_opt_hdr {
>> +       __u8            nd_opt_type;
>> +       __u8            nd_opt_len;
>> +} __attribute__((__packed__));
>> +
>> +extern struct in6_addr const net_null_addr_ip6;        /* NULL IPv6 address */
>> +extern struct in6_addr net_gateway6;   /* Our gateways IPv6 address */
>> +extern struct in6_addr net_ip6;        /* Our IPv6 addr (0 = unknown) */
>> +extern struct in6_addr net_link_local_ip6;     /* Our link local IPv6 addr */
>> +extern u_int32_t net_prefix_length;    /* Our prefixlength (0 = unknown) */
>> +extern struct in6_addr net_server_ip6; /* Server IPv6 addr (0 = unknown) */
>> +
>> +#ifdef CONFIG_CMD_PING
>> +extern struct in6_addr net_ping_ip6;   /* the ipv6 address to ping */
>> +#endif
>> +
>>  /* ::ffff:0:0/96 is reserved for v4 mapped addresses */
>>  static inline int ipv6_addr_v4mapped(const struct in6_addr *a)
>>  {
>> @@ -61,4 +219,58 @@ static inline int ipv6_addr_is_isatap(const struct in6_addr *a)
>>  /* Convert a string to an ipv6 address */
>>  int string_to_ip6(const char *s, struct in6_addr *addr);
>>
>> +/* check that an IPv6 address is unspecified (zero) */
>> +int ip6_is_unspecified_addr(struct in6_addr *addr);
>> +
>> +/* check that an IPv6 address is ours */
>> +int ip6_is_our_addr(struct in6_addr *addr);
>> +
>> +void ip6_make_lladdr(struct in6_addr *lladr, unsigned char const enetaddr[6]);
>> +
>> +void ip6_make_SNMA(struct in6_addr *mcast_addr, struct in6_addr *ip6_addr);
>> +
>> +void ip6_make_mult_ethdstaddr(unsigned char enetaddr[6],
>> +                             struct in6_addr *mcast_addr);
>> +
>> +/* check if neighbour is in the same subnet as us */
>> +int ip6_addr_in_subnet(struct in6_addr *our_addr, struct in6_addr *neigh_addr,
>> +                      __u32 prefix_length);
>> +
>> +unsigned int csum_partial(const unsigned char *buff, int len, unsigned int sum);
>> +
>> +unsigned short int csum_ipv6_magic(struct in6_addr *saddr,
>> +                                  struct in6_addr *daddr, __u16 len,
>> +                                  unsigned short proto, unsigned int csum);
>> +
>> +int ip6_add_hdr(uchar *xip, struct in6_addr *src, struct in6_addr *dest,
>> +               int nextheader, int hoplimit, int payload_len);
>> +
>> +/* send a neighbour discovery solicitation message */
>> +void ip6_NDISC_Request(void);
>> +
>> +/* call back routine when ND timer has gone off */
>> +void ip6_NDISC_TimeoutCheck(void);
>> +
>> +/* initialises the ND data */
>> +void ip6_NDISC_init(void);
>> +
>> +/* sends an IPv6 echo request to a host */
>> +int ping6_send(void);
>> +
>> +/* starts a Ping6 process */
>> +void ping6_start(void);
>> +
>> +/* handles reception of icmpv6 echo request/reply */
>> +void ping6_receive(struct ethernet_hdr *et, struct ip6_hdr *ip6,
>> +                         int len);
>> +
>> +/* handler for incoming IPv6 echo packet */
>> +void net_ip6_handler(struct ethernet_hdr *et, struct ip6_hdr *ip6,
>> +                           int len);
>> +
>> +/* copy IPv6 */
>> +static inline void net_copy_ip6(void *to, void *from)
>> +{
>> +       memcpy((void *)to, from, sizeof(struct in6_addr));
>> +}
>>  #endif /* __NET6_H__ */
>> diff --git a/net/Kconfig b/net/Kconfig
>> index 77a2f7e..ee198c1 100644
>> --- a/net/Kconfig
>> +++ b/net/Kconfig
>> @@ -22,4 +22,9 @@ config NETCONSOLE
>>           Support the 'nc' input/output device for networked console.
>>           See README.NetConsole for details.
>>
>> +config NET6
>> +       bool "IPv6 support"
>> +       help
>> +         Support for IPv6
>> +
>>  endif   # if NET
>> diff --git a/net/Makefile b/net/Makefile
>> index e9cc8ad..a85a5c6 100644
>> --- a/net/Makefile
>> +++ b/net/Makefile
>> @@ -20,3 +20,6 @@ obj-$(CONFIG_CMD_PING) += ping.o
>>  obj-$(CONFIG_CMD_RARP) += rarp.o
>>  obj-$(CONFIG_CMD_SNTP) += sntp.o
>>  obj-$(CONFIG_CMD_NET)  += tftp.o
>> +obj-$(CONFIG_CMD_NET6) += net6.o
>> +obj-$(CONFIG_CMD_NET6) += ndisc.o
>
> I'd like to see the stuff that is net core (not a command) start using
> CONFIG_NET6 instead of CONFIG_CMD_NET6.
>

Understood.

>> +obj-$(CONFIG_CMD_PING6) += ping6.o
>> diff --git a/net/ndisc.c b/net/ndisc.c
>> new file mode 100644
>> index 0000000..41883d7
>> --- /dev/null
>> +++ b/net/ndisc.c
>> @@ -0,0 +1,269 @@
>> +/*
>> + * net/ndisc.c
>> + *
>> + * (C) Copyright 2013 Allied Telesis Labs NZ
>> + *
>> + * SPDX-License-Identifier:    GPL-2.0+
>> + */
>> +#define DEBUG
>> +#include <common.h>
>> +#include <net.h>
>> +#include <net6.h>
>> +#include "ndisc.h"
>> +
>> +/* IPv6 destination address of packet waiting for ND */
>> +struct in6_addr net_nd_sol_packet_ip6 = ZERO_IPV6_ADDR;
>> +/* IPv6 address we are expecting ND advert from */
>> +struct in6_addr net_nd_rep_packet_ip6 = ZERO_IPV6_ADDR;
>
> static. The same goes for any others that are not needed outside of this file.
>
>> +/* MAC destination address of packet waiting for ND */
>> +uchar *net_nd_packet_mac;
>> +/* pointer to packet waiting to be transmitted after ND is resolved */
>> +uchar *net_nd_tx_packet;
>> +uchar net_nd_packet_buf[PKTSIZE_ALIGN + PKTALIGN];
>> +/* size of packet waiting to be transmitted */
>> +int net_nd_tx_packet_size;
>> +/* the timer for ND resolution */
>> +ulong net_nd_timer_start;
>> +/* the number of requests we have sent so far */
>> +int net_nd_try;
>> +
>> +#define IP6_NDISC_OPT_SPACE(len) (((len)+2+7)&~7)
>> +
>> +/**
>> + * Insert an iption into a neighbor discovery packet.
>
> iption -> option
>
>> + * Returns the number of bytes inserted (which may be >= len)
>> + */
>> +static int
>> +ip6_ndisc_insert_option(struct nd_msg *ndisc, int type, u8 *data, int len)
>> +{
>> +       int space = IP6_NDISC_OPT_SPACE(len);
>> +
>> +       ndisc->opt[0] = type;
>> +       ndisc->opt[1] = space >> 3;
>> +       memcpy(&ndisc->opt[2], data, len);
>> +       len += 2;
>> +
>> +       /* fill the remainder with 0 */
>> +       if ((space - len) > 0)
>> +               memset(&ndisc->opt[len], 0, space - len);
>> +
>> +       return space;
>> +}
>> +
>> +/**
>> + * Extract the Ethernet address from a neighbor discovery packet.
>> + * Note that the link layer address could be anything but the only networking
>> + * media that u-boot supports is Ethernet so we assume we're extracting a 6
>> + * byte Ethernet MAC address.
>> + */
>> +static void ip6_ndisc_extract_enetaddr(struct nd_msg *ndisc, uchar enetaddr[6])
>> +{
>> +       memcpy(enetaddr, &ndisc->opt[2], 6);
>> +}
>> +
>> +/**
>> + * Check to see if the neighbor discovery packet has
>> + * the specified option set.
>> + */
>> +static int ip6_ndisc_has_option(struct ip6_hdr *ip6, __u8 type)
>> +{
>> +       struct nd_msg *ndisc = (struct nd_msg *)(((uchar *)ip6) + IP6_HDR_SIZE);
>> +
>> +       if (ip6->payload_len <= sizeof(struct icmp6hdr))
>> +               return 0;
>> +
>> +       return ndisc->opt[0] == type;
>> +}
>> +
>> +static void ip6_send_ns(struct in6_addr *neigh_addr)
>> +{
>> +       struct in6_addr dst_adr;
>> +       unsigned char enetaddr[6];
>> +       struct nd_msg *msg;
>> +       __u16 len;
>> +       uchar *pkt;
>> +
>> +       debug("sending neighbor solicitation for %pI6c our address %pI6c\n",
>> +             neigh_addr, &net_link_local_ip6);
>> +
>> +       /* calculate src, dest IPv6 addr and dest Eth addr */
>> +       ip6_make_SNMA(&dst_adr, neigh_addr);
>> +       ip6_make_mult_ethdstaddr(enetaddr, &dst_adr);
>> +       len = sizeof(struct icmp6hdr) + IN6ADDRSZ +
>> +           IP6_NDISC_OPT_SPACE(INETHADDRSZ);
>> +
>> +       pkt = (uchar *)net_tx_packet;
>> +       pkt += net_set_ether(pkt, enetaddr, PROT_IP6);
>> +       pkt += ip6_add_hdr(pkt, &net_link_local_ip6, &dst_adr, IPPROTO_ICMPV6,
>> +                          IPV6_NDISC_HOPLIMIT, len);
>> +
>> +       /* ICMPv6 - NS */
>> +       msg = (struct nd_msg *)pkt;
>> +       msg->icmph.icmp6_type = IPV6_NDISC_NEIGHBOUR_SOLICITATION;
>> +       msg->icmph.icmp6_code = 0;
>> +       memset(&msg->icmph.icmp6_cksum, 0, sizeof(__be16));
>> +       memset(&msg->icmph.icmp6_unused, 0, sizeof(__be32));
>> +
>> +       /* Set the target address and llsaddr option */
>> +       net_copy_ip6(&msg->target, neigh_addr);
>> +       ip6_ndisc_insert_option(msg, ND_OPT_SOURCE_LL_ADDR, net_ethaddr,
>> +                               INETHADDRSZ);
>> +
>> +       /* checksum */
>> +       msg->icmph.icmp6_cksum = csum_ipv6_magic(&net_link_local_ip6, &dst_adr,
>> +                                                len, IPPROTO_ICMPV6,
>> +                                                csum_partial((__u8 *)msg, len, 0));
>> +
>> +       pkt += len;
>> +
>> +       /* send it! */
>> +       net_send_packet(net_tx_packet, (pkt - net_tx_packet));
>> +}
>> +
>> +static void
>> +ip6_send_na(uchar *eth_dst_addr, struct in6_addr *neigh_addr,
>> +           struct in6_addr *target)
>> +{
>> +       struct nd_msg *msg;
>> +       __u16 len;
>> +       uchar *pkt;
>> +
>> +       debug("sending neighbor advertisement for %pI6c to %pI6c (%pM)\n",
>> +             target, neigh_addr, eth_dst_addr);
>> +
>> +       len = sizeof(struct icmp6hdr) + IN6ADDRSZ +
>> +           IP6_NDISC_OPT_SPACE(INETHADDRSZ);
>> +
>> +       pkt = (uchar *)net_tx_packet;
>> +       pkt += net_set_ether(pkt, eth_dst_addr, PROT_IP6);
>> +       pkt += ip6_add_hdr(pkt, &net_link_local_ip6, neigh_addr,
>> +                          IPPROTO_ICMPV6, IPV6_NDISC_HOPLIMIT, len);
>> +
>> +       /* ICMPv6 - NS */
>
> Probably mean to say "ICMPv6 - NA" here, right?
>
>> +       msg = (struct nd_msg *)pkt;
>> +       msg->icmph.icmp6_type = IPV6_NDISC_NEIGHBOUR_ADVERTISEMENT;
>> +       msg->icmph.icmp6_code = 0;
>> +       memset(&msg->icmph.icmp6_cksum, 0, sizeof(__be16));
>> +       memset(&msg->icmph.icmp6_unused, 0, sizeof(__be32));
>> +
>> +       /* Set the target address and lltargetaddr option */
>> +       net_copy_ip6(&msg->target, target);
>> +       ip6_ndisc_insert_option(msg, ND_OPT_TARGET_LL_ADDR, net_ethaddr,
>> +                               INETHADDRSZ);
>> +
>> +       /* checksum */
>> +       msg->icmph.icmp6_cksum = csum_ipv6_magic(&net_link_local_ip6,
>> +                                                neigh_addr, len, IPPROTO_ICMPV6,
>> +                                                csum_partial((__u8 *)msg, len, 0));
>> +
>> +       pkt += len;
>> +
>> +       /* send it! */
>> +       net_send_packet(net_tx_packet, (pkt - net_tx_packet));
>> +}
>> +
>> +void ip6_NDISC_Request(void)
>
> Please don't use CamelCase looking stuff. All lower case is usually appropriate.
>

Will fix. The original Allied Telesis implementation was based on a
fairly old verison and copied the non-standard net.c which was
recently updated to eliminate camelcase.

Also I was wondering about "ip6_ndisc" vs "ndisc". The latter is
sorter and kind of fits with "arp" for ipv4 but the difference is that
unlike arp, neigbor discovery actually operates over ipv6.

>> +{
>> +       if (!ip6_addr_in_subnet(&net_ip6, &net_nd_sol_packet_ip6,
>> +                               net_prefix_length)) {
>> +               if (ip6_is_unspecified_addr(&net_gateway6)) {
>> +                       puts("## Warning: gatewayip6 is needed but not set\n");
>> +                       net_nd_rep_packet_ip6 = net_nd_sol_packet_ip6;
>
> Is this just assuming that since there is no gateway that the device
> might be on the local segment even though the address is not on our
> subnet?
>

Yeah I might need to add some conditions on the configured address not
being the link local one.

>> +               } else {
>> +                       net_nd_rep_packet_ip6 = net_gateway6;
>> +               }
>> +       } else {
>> +               net_nd_rep_packet_ip6 = net_nd_sol_packet_ip6;
>> +       }
>> +
>> +       ip6_send_ns(&net_nd_rep_packet_ip6);
>> +}
>> +
>> +void ip6_NDISC_TimeoutCheck(void)
>
> Lowercase.
>

Yep will fix all occurences in the next round.

>> +{
>> +       ulong t;
>> +
>> +       if (ip6_is_unspecified_addr(&net_nd_sol_packet_ip6))
>> +               return;
>> +
>> +       t = get_timer(0);
>> +
>> +       /* check for NDISC timeout */
>> +       if ((t - net_nd_timer_start) > NDISC_TIMEOUT) {
>> +               net_nd_try++;
>> +               if (net_nd_try >= NDISC_TIMEOUT_COUNT) {
>> +                       puts("\nNeighbour discovery retry count exceeded; "
>> +                            "starting again\n");
>> +                       net_nd_try = 0;
>> +                       net_start_again();
>> +               } else {
>> +                       net_nd_timer_start = t;
>> +                       ip6_NDISC_Request();
>> +               }
>> +       }
>> +}
>> +
>> +void ip6_NDISC_init(void)
>
> Lowercase.
>
>> +{
>> +       net_nd_packet_mac = NULL;
>> +       net_nd_tx_packet = NULL;
>> +       net_nd_sol_packet_ip6 = net_null_addr_ip6;
>> +       net_nd_rep_packet_ip6 = net_null_addr_ip6;
>> +       net_nd_tx_packet = NULL;
>> +
>> +       if (!net_nd_tx_packet) {
>> +               net_nd_tx_packet = &net_nd_packet_buf[0] + (PKTALIGN - 1);
>> +               net_nd_tx_packet -= (ulong)net_nd_tx_packet % PKTALIGN;
>> +               net_nd_tx_packet_size = 0;
>> +       }
>> +}
>> +
>> +void ndisc_receive(struct ethernet_hdr *et, struct ip6_hdr *ip6, int len)
>> +{
>> +       struct icmp6hdr *icmp =
>> +           (struct icmp6hdr *)(((uchar *)ip6) + IP6_HDR_SIZE);
>> +       struct nd_msg *ndisc = (struct nd_msg *)icmp;
>> +       uchar neigh_eth_addr[6];
>> +
>> +       switch (icmp->icmp6_type) {
>> +       case IPV6_NDISC_NEIGHBOUR_SOLICITATION:
>> +               debug("received neighbor solicitation for %pI6c from %pI6c\n",
>> +                     &ndisc->target, &ip6->saddr);
>> +               if (ip6_is_our_addr(&ndisc->target) &&
>> +                   ip6_ndisc_has_option(ip6, ND_OPT_SOURCE_LL_ADDR)) {
>> +                       ip6_ndisc_extract_enetaddr(ndisc, neigh_eth_addr);
>> +                       ip6_send_na(neigh_eth_addr, &ip6->saddr,
>> +                                   &ndisc->target);
>> +               }
>> +               break;
>> +
>> +       case IPV6_NDISC_NEIGHBOUR_ADVERTISEMENT:
>> +               /* are we waiting for a reply ? */
>> +               if (ip6_is_unspecified_addr(&net_nd_sol_packet_ip6))
>> +                       break;
>> +
>> +               if ((memcmp(&ndisc->target, &net_nd_rep_packet_ip6,
>> +                           sizeof(struct in6_addr)) == 0) &&
>> +                   ip6_ndisc_has_option(ip6, ND_OPT_TARGET_LL_ADDR)) {
>> +                       ip6_ndisc_extract_enetaddr(ndisc, neigh_eth_addr);
>> +
>> +                       /* save address for later use */
>> +                       if (net_nd_packet_mac != NULL)
>> +                               memcpy(net_nd_packet_mac, neigh_eth_addr, 6);
>> +
>> +                       /* modify header, and transmit it */
>> +                       memcpy(((struct ethernet_hdr *)net_nd_tx_packet)->et_dest,
>> +                              neigh_eth_addr, 6);
>> +                       net_send_packet(net_nd_tx_packet,
>> +                                       net_nd_tx_packet_size);
>> +
>> +                       /* no ND request pending now */
>> +                       net_nd_sol_packet_ip6 = net_null_addr_ip6;
>> +                       net_nd_tx_packet_size = 0;
>> +                       net_nd_packet_mac = NULL;
>> +               }
>> +               break;
>> +       default:
>> +               debug("Unexpected ICMPv6 type 0x%x\n", icmp->icmp6_type);
>> +       }
>> +}
>> diff --git a/net/ndisc.h b/net/ndisc.h
>> new file mode 100644
>> index 0000000..7ade0fc
>> --- /dev/null
>> +++ b/net/ndisc.h
>> @@ -0,0 +1,27 @@
>> +/*
>> + * net/ndisc.h
>> + *
>> + * (C) Copyright 2013 Allied Telesis Labs NZ
>> + *
>> + * SPDX-License-Identifier:    GPL-2.0+
>> + */
>> +
>> +/* IPv6 destination address of packet waiting for ND */
>> +extern struct in6_addr net_nd_sol_packet_ip6;
>> +/* IPv6 address we are expecting ND advert from */
>> +extern struct in6_addr net_nd_rep_packet_ip6;
>
> This is not used outside of ndisc.c so remove it from this header.
>
> The same goes for any others in here that are not needed outside.
>
>> +/* MAC destination address of packet waiting for ND */
>> +extern uchar *net_nd_packet_mac;
>> +/* pointer to packet waiting to be transmitted after ND is resolved */
>> +extern uchar *net_nd_tx_packet;
>> +extern uchar net_nd_packet_buf[PKTSIZE_ALIGN + PKTALIGN];
>> +/* size of packet waiting to be transmitted */
>> +extern int net_nd_tx_packet_size;
>> +/* the timer for ND resolution */
>> +extern ulong net_nd_timer_start;
>> +/* the number of requests we have sent so far */
>> +extern int net_nd_try;
>> +
>> +void ndisc_receive(struct ethernet_hdr *et, struct ip6_hdr *ip6, int len);
>> +void ip6_NDISC_Request(void);
>> +void ip6_NDISC_TimeoutCheck(void);
>> diff --git a/net/net.c b/net/net.c
>> index a115ce2..349a18e 100644
>> --- a/net/net.c
>> +++ b/net/net.c
>> @@ -86,6 +86,7 @@
>>  #include <environment.h>
>>  #include <errno.h>
>>  #include <net.h>
>> +#include <net6.h>
>>  #include <net/tftp.h>
>>  #if defined(CONFIG_STATUS_LED)
>>  #include <miiphy.h>
>> @@ -94,6 +95,7 @@
>>  #include <watchdog.h>
>>  #include <linux/compiler.h>
>>  #include "arp.h"
>> +#include "ndisc.h"
>>  #include "bootp.h"
>>  #include "cdp.h"
>>  #if defined(CONFIG_CMD_DNS)
>> @@ -311,6 +313,7 @@ static int on_dnsip(const char *name, const char *value, enum env_op op,
>>  U_BOOT_ENV_CALLBACK(dnsip, on_dnsip);
>>  #endif
>>
>> +
>
> No need to add white space here.
>
>>  /*
>>   * Check if autoload is enabled. If so, use either NFS or TFTP to download
>>   * the boot file.
>> @@ -341,8 +344,12 @@ void net_auto_load(void)
>>
>>  static void net_init_loop(void)
>>  {
>> -       if (eth_get_dev())
>> +       if (eth_get_dev()) {
>>                 memcpy(net_ethaddr, eth_get_ethaddr(), 6);
>> +#ifdef CONFIG_CMD_NET6
>> +               ip6_make_lladdr(&net_link_local_ip6, net_ethaddr);
>> +#endif
>> +       }
>>
>>         return;
>>  }
>> @@ -376,6 +383,9 @@ void net_init(void)
>>                                 (i + 1) * PKTSIZE_ALIGN;
>>                 }
>>                 arp_init();
>> +#ifdef CONFIG_CMD_NET6
>> +               ip6_NDISC_init();
>> +#endif
>>                 net_clear_handlers();
>>
>>                 /* Only need to setup buffer pointers once. */
>> @@ -478,6 +488,11 @@ restart:
>>                         ping_start();
>>                         break;
>>  #endif
>> +#ifdef CONFIG_CMD_PING6
>> +               case PING6:
>> +                       ping6_start();
>> +                       break;
>> +#endif
>>  #if defined(CONFIG_CMD_NFS)
>>                 case NFS:
>>                         nfs_start();
>> @@ -555,6 +570,9 @@ restart:
>>                 if (ctrlc()) {
>>                         /* cancel any ARP that may not have completed */
>>                         net_arp_wait_packet_ip.s_addr = 0;
>> +#ifdef CONFIG_CMD_NET6
>> +                       net_nd_sol_packet_ip6 = net_null_addr_ip6;
>> +#endif
>>
>>                         net_cleanup_loop();
>>                         eth_halt();
>> @@ -570,6 +588,9 @@ restart:
>>                 }
>>
>>                 arp_timeout_check();
>> +#ifdef CONFIG_CMD_NET6
>> +               ip6_NDISC_TimeoutCheck();
>> +#endif
>>
>>                 /*
>>                  *      Check for a timeout, and run the timeout handler
>> @@ -1141,6 +1162,11 @@ void net_process_received_packet(uchar *in_packet, int len)
>>                 rarp_receive(ip, len);
>>                 break;
>>  #endif
>> +#ifdef CONFIG_CMD_NET6
>> +       case PROT_IP6:
>> +               net_ip6_handler(et, (struct ip6_hdr *)ip, len);
>> +               break;
>> +#endif
>>         case PROT_IP:
>>                 debug_cond(DEBUG_NET_PKT, "Got IP\n");
>>                 /* Before we start poking the header, make sure it is there */
>> @@ -1295,6 +1321,14 @@ static int net_check_prereq(enum proto_t protocol)
>>                 }
>>                 goto common;
>>  #endif
>> +#ifdef CONFIG_CMD_PING6
>> +       case PING6:
>> +               if (ip6_is_unspecified_addr(&net_ping_ip6)) {
>> +                       puts("*** ERROR: ping address not given\n");
>> +                       return 1;
>> +               }
>> +               goto common;
>> +#endif
>
> All the ping stuff seems like it could come out into a separate patch.
>

Agreed.

>>  #if defined(CONFIG_CMD_SNTP)
>>         case SNTP:
>>                 if (net_ntp_server.s_addr == 0) {
>> diff --git a/net/net6.c b/net/net6.c
>> new file mode 100644
>> index 0000000..2315704
>> --- /dev/null
>> +++ b/net/net6.c
>> @@ -0,0 +1,375 @@
>> +/*
>> + * Simple IPv6 network layer implementation.
>> + *
>> + * Based and/or adapted from the IPv4 network layer in net.[hc]
>> + *
>> + * (C) Copyright 2013 Allied Telesis Labs NZ
>> + *
>> + * SPDX-License-Identifier:    GPL-2.0+
>> + */
>> +
>> +/*
>> + * General Desription:
>> + *
>> + * The user interface supports commands for TFTP6.
>> + * Also, we support Neighbour discovery internally. Depending on available
>> + * data, these interact as follows:
>> + *
>> + * Neighbour Discovery:
>> + *
>> + *      Prerequisites:  - own ethernet address
>> + *                      - own IPv6 address
>> + *                      - TFTP server IPv6 address
>> + *      We want:        - TFTP server ethernet address
>> + *      Next step:      TFTP
>> + *
>> + * TFTP over IPv6:
>> + *
>> + *      Prerequisites:  - own ethernet address
>> + *                      - own IPv6 address
>> + *                      - TFTP server IPv6 address
>> + *                      - TFTP server ethernet address
>> + *                      - name of bootfile (if unknown, we use a default name
>> + *                        derived from our own IPv6 address)
>> + *      We want:        - load the boot file
>> + *      Next step:      none
>> + *
>> + */
>> +#define DEBUG
>> +#include <common.h>
>> +#include <environment.h>
>> +#include <net.h>
>> +#include <net6.h>
>> +#include "ndisc.h"
>> +
>> +/* NULL IPv6 address */
>> +struct in6_addr const net_null_addr_ip6 = ZERO_IPV6_ADDR;
>> +/* Our gateway's IPv6 address */
>> +struct in6_addr net_gateway6 = ZERO_IPV6_ADDR;
>> +/* Our IPv6 addr (0 = unknown) */
>> +struct in6_addr net_ip6 = ZERO_IPV6_ADDR;
>> +/* Our link local IPv6 addr (0 = unknown) */
>> +struct in6_addr net_link_local_ip6 = ZERO_IPV6_ADDR;
>> +/* set server IPv6 addr (0 = unknown) */
>> +struct in6_addr net_server_ip6 = ZERO_IPV6_ADDR;
>> +/* The prefix length of our network */
>> +u_int32_t net_prefix_length;
>> +
>> +static int on_ip6addr(const char *name, const char *value, enum env_op op,
>> +                     int flags)
>> +{
>> +       if (flags & H_PROGRAMMATIC)
>> +               return 0;
>> +
>> +       return string_to_ip6(value, &net_ip6);
>> +}
>> +
>> +U_BOOT_ENV_CALLBACK(ip6addr, on_ip6addr);
>> +
>> +static int on_gatewayip6(const char *name, const char *value, enum env_op op,
>> +                        int flags)
>> +{
>> +       if (flags & H_PROGRAMMATIC)
>> +               return 0;
>> +
>> +       return string_to_ip6(value, &net_gateway6);
>> +}
>> +
>> +U_BOOT_ENV_CALLBACK(gatewayip6, on_gatewayip6);
>> +
>> +static int on_prefixlength6(const char *name, const char *value, enum env_op op,
>> +                           int flags)
>> +{
>> +       if (flags & H_PROGRAMMATIC)
>> +               return 0;
>> +
>> +       net_prefix_length = simple_strtoul(value, NULL, 10);
>> +
>> +       return 0;
>> +}
>> +
>> +U_BOOT_ENV_CALLBACK(prefixlength6, on_prefixlength6);
>> +
>> +static int on_serverip6(const char *name, const char *value, enum env_op op,
>> +                       int flags)
>> +{
>> +       if (flags & H_PROGRAMMATIC)
>> +               return 0;
>> +
>> +       return string_to_ip6(value, &net_server_ip6);
>> +}
>> +
>> +U_BOOT_ENV_CALLBACK(serverip6, on_serverip6);
>> +
>> +int ip6_is_unspecified_addr(struct in6_addr *addr)
>> +{
>> +       return (addr->s6_addr32[0] | addr->s6_addr32[1] |
>> +               addr->s6_addr32[2] | addr->s6_addr32[3]) == 0;
>> +}
>> +
>> +/**
>> + * We have 2 addresses that we should respond to. A link
>> + * local address and a global address. This returns true
>> + * if the specified address matches either of these.
>> + */
>> +int ip6_is_our_addr(struct in6_addr *addr)
>> +{
>> +       return memcmp(addr, &net_link_local_ip6, sizeof(struct in6_addr)) == 0 ||
>> +              memcmp(addr, &net_ip6, sizeof(struct in6_addr)) == 0;
>> +}
>> +
>> +void ip6_make_eui(unsigned char eui[8], unsigned char const enetaddr[6])
>> +{
>> +       memcpy(eui, enetaddr, 3);
>> +       memcpy(&eui[5], &enetaddr[3], 3);
>> +       eui[3] = 0xFF;
>> +       eui[4] = 0xFE;
>> +       eui[0] ^= 2;            /* "u" bit set to indicate global scope */
>> +}
>> +
>> +void ip6_make_lladdr(struct in6_addr *lladr, unsigned char const enetaddr[6])
>> +{
>> +       uchar eui[8];
>> +
>> +       memset(lladr, 0, sizeof(struct in6_addr));
>> +       lladr->s6_addr16[0] = htons(IPV6_LINK_LOCAL_PREFIX);
>> +       ip6_make_eui(eui, enetaddr);
>> +       memcpy(&lladr->s6_addr[8], eui, 8);
>> +}
>> +
>> +/*
>> + * Given an IPv6 address generate an equivalent Solicited Node Multicast
>> + * Address (SNMA) as described in RFC2461.
>> + */
>> +void ip6_make_SNMA(struct in6_addr *mcast_addr, struct in6_addr *ip6_addr)
>
> Lowercase.
>
>> +{
>> +       memset(mcast_addr, 0, sizeof(struct in6_addr));
>> +       mcast_addr->s6_addr[0] = 0xff;
>> +       mcast_addr->s6_addr[1] = IPV6_ADDRSCOPE_LINK;
>> +       mcast_addr->s6_addr[11] = 0x01;
>> +       mcast_addr->s6_addr[12] = 0xff;
>> +       mcast_addr->s6_addr[13] = ip6_addr->s6_addr[13];
>> +       mcast_addr->s6_addr[14] = ip6_addr->s6_addr[14];
>> +       mcast_addr->s6_addr[15] = ip6_addr->s6_addr[15];
>> +}
>> +
>> +/*
>> + * Given an IPv6 address generate the multicast MAC address that corresponds to
>> + * it.
>> + */
>> +void
>> +ip6_make_mult_ethdstaddr(unsigned char enetaddr[6], struct in6_addr *mcast_addr)
>> +{
>> +       enetaddr[0] = 0x33;
>> +       enetaddr[1] = 0x33;
>> +       memcpy(&enetaddr[2], &mcast_addr->s6_addr[12], 4);
>> +}
>> +
>> +int
>> +ip6_addr_in_subnet(struct in6_addr *our_addr, struct in6_addr *neigh_addr,
>> +                  __u32 plen)
>> +{
>> +       __be32 *addr_dwords;
>> +       __be32 *neigh_dwords;
>> +
>> +       addr_dwords = our_addr->s6_addr32;
>> +       neigh_dwords = neigh_addr->s6_addr32;
>> +
>> +       while (plen > 32) {
>> +               if (*addr_dwords++ != *neigh_dwords++)
>> +                       return 0;
>> +
>> +               plen -= 32;
>> +       }
>> +
>> +       /* Check any remaining bits. */
>> +       if (plen > 0) {
>> +               /* parameters are in network byte order.
>> +                  Does this work on a LE host? */
>
> So is this still an outstanding question? Probably worth testing on a
> LE target, but it should work I believe.
>

I'm actually testing with x86 on QEMU so I think LE is all good. I'll
remove the comment.

>> +               if ((*addr_dwords >> (32 - plen)) !=
>> +                   (*neigh_dwords >> (32 - plen))) {
>> +                       return 0;
>> +               }
>> +       }
>> +
>> +       return 1;
>> +}
>> +
>> +static inline unsigned int csum_fold(unsigned int sum)
>> +{
>> +       sum = (sum & 0xffff) + (sum >> 16);
>> +       sum = (sum & 0xffff) + (sum >> 16);
>> +
>> +       return ~sum;
>> +}
>> +
>> +static __u32 csum_do_csum(const __u8 *buff, int len)
>> +{
>> +       int odd, count;
>> +       unsigned long result = 0;
>> +
>> +       if (len <= 0)
>> +               goto out;
>> +       odd = 1 & (unsigned long)buff;
>> +       if (odd) {
>> +               result = *buff;
>> +               len--;
>> +               buff++;
>> +       }
>> +       count = len >> 1;       /* nr of 16-bit words.. */
>> +       if (count) {
>> +               if (2 & (unsigned long)buff) {
>> +                       result += *(unsigned short *)buff;
>> +                       count--;
>> +                       len -= 2;
>> +                       buff += 2;
>> +               }
>> +               count >>= 1;    /* nr of 32-bit words.. */
>> +               if (count) {
>> +                       unsigned long carry = 0;
>> +                       do {
>> +                               unsigned long w = *(unsigned long *)buff;
>> +                               count--;
>> +                               buff += 4;
>> +                               result += carry;
>> +                               result += w;
>> +                               carry = (w > result);
>> +                       } while (count);
>> +                       result += carry;
>> +                       result = (result & 0xffff) + (result >> 16);
>> +               }
>> +               if (len & 2) {
>> +                       result += *(unsigned short *)buff;
>> +                       buff += 2;
>> +               }
>> +       }
>> +       if (len & 1)
>> +               result += (*buff << 8);
>> +       result = ~csum_fold(result);
>> +       if (odd)
>> +               result = ((result >> 8) & 0xff) | ((result & 0xff) << 8);
>> +out:
>> +       return result;
>> +}
>> +
>> +unsigned int csum_partial(const unsigned char *buff, int len, unsigned int sum)
>> +{
>> +       unsigned int result = csum_do_csum(buff, len);
>> +
>> +       /* add in old sum, and carry.. */
>> +       result += sum;
>> +       /* 16+c bits -> 16 bits */
>> +       result = (result & 0xffff) + (result >> 16);
>> +       return result;
>> +}
>> +
>> +unsigned short int
>> +csum_ipv6_magic(struct in6_addr *saddr, struct in6_addr *daddr,
>> +               __u16 len, unsigned short proto, unsigned int csum)
>> +{
>
> Please add some comments to this to make it less magic. :)
>

I think there should be an RFC to describe this. I'll look it up and
put anything relevant here.

>> +       int i;
>> +       int carry;
>> +       __u32 ulen;
>> +       __u32 uproto;
>> +       unsigned int finalsum;
>> +
>> +       for (i = 0; i < 4; i++) {
>> +               csum += saddr->s6_addr32[i];
>> +               carry = (csum < saddr->s6_addr32[i]);
>> +               csum += carry;
>> +
>> +               csum += daddr->s6_addr32[i];
>> +               carry = (csum < daddr->s6_addr32[i]);
>> +               csum += carry;
>> +       }
>> +
>> +       ulen = htonl((__u32)len);
>> +       csum += ulen;
>> +       carry = (csum < ulen);
>> +       csum += carry;
>> +
>> +       uproto = htonl(proto);
>> +       csum += uproto;
>> +       carry = (csum < uproto);
>> +       csum += carry;
>> +
>> +       finalsum = csum_fold(csum);
>> +       if ((finalsum & 0xffff) == 0x0000)
>> +               return 0xffff;
>> +       else if ((finalsum & 0xffff) == 0xffff)
>> +               return 0x0000;
>> +       else
>> +               return finalsum;
>> +}
>> +
>> +int
>> +ip6_add_hdr(uchar *xip, struct in6_addr *src, struct in6_addr *dest,
>> +           int nextheader, int hoplimit, int payload_len)
>> +{
>> +       struct ip6_hdr *ip6 = (struct ip6_hdr *)xip;
>> +
>> +       ip6->version = 6;
>> +       ip6->priority = 0;
>> +       ip6->flow_lbl[0] = 0;
>> +       ip6->flow_lbl[1] = 0;
>> +       ip6->flow_lbl[2] = 0;
>> +       ip6->payload_len = htons(payload_len);
>> +       ip6->nexthdr = nextheader;
>> +       ip6->hop_limit = hoplimit;
>> +       net_copy_ip6(&ip6->saddr, src);
>> +       net_copy_ip6(&ip6->daddr, dest);
>> +
>> +       return sizeof(struct ip6_hdr);
>> +}
>> +
>> +void net_ip6_handler(struct ethernet_hdr *et, struct ip6_hdr *ip6, int len)
>> +{
>> +       struct in_addr zero_ip = {.s_addr = 0 };
>> +       struct icmp6hdr *icmp;
>> +       struct udp_hdr *udp;
>> +       __u16 csum;
>> +       __u16 hlen;
>> +
>> +       if (len < IP6_HDR_SIZE)
>> +               return;
>> +
>> +       if (ip6->version != 6)
>> +               return;
>> +
>> +       switch (ip6->nexthdr) {
>> +       case IPPROTO_ICMPV6:
>> +               icmp = (struct icmp6hdr *)(((uchar *)ip6) + IP6_HDR_SIZE);
>> +               csum = icmp->icmp6_cksum;
>> +               hlen = ntohs(ip6->payload_len);
>> +               icmp->icmp6_cksum = 0;
>> +               /* checksum */
>> +               icmp->icmp6_cksum = csum_ipv6_magic(&ip6->saddr, &ip6->daddr,
>> +                                                   hlen, IPPROTO_ICMPV6,
>> +                                                   csum_partial((__u8 *)icmp, hlen, 0));
>> +               if (icmp->icmp6_cksum != csum)
>> +                       return;
>> +
>> +               switch (icmp->icmp6_type) {
>> +#ifdef CONFIG_CMD_PING6
>> +               case IPV6_ICMP_ECHO_REQUEST:
>> +               case IPV6_ICMP_ECHO_REPLY:
>> +                       ping6_receive(et, ip6, len);
>> +                       break;
>> +#endif /* CONFIG_CMD_PING6 */
>> +
>> +               case IPV6_NDISC_NEIGHBOUR_SOLICITATION:
>> +               case IPV6_NDISC_NEIGHBOUR_ADVERTISEMENT:
>> +                       ndisc_receive(et, ip6, len);
>> +                       break;
>> +
>> +               default:
>> +                       return;
>> +                       break;
>> +               }
>> +               break;
>> +
>> +       default:
>> +               return;
>> +               break;
>> +       }
>> +}
>> diff --git a/net/ping6.c b/net/ping6.c
>> new file mode 100644
>> index 0000000..aa93dfa
>> --- /dev/null
>> +++ b/net/ping6.c
>> @@ -0,0 +1,111 @@
>> +/*
>> + * net/ping6.c
>> + *
>> + * (C) Copyright 2013 Allied Telesis Labs NZ
>> + *
>> + * SPDX-License-Identifier:    GPL-2.0+
>> + */
>> +#define DEBUG
>> +#include <common.h>
>> +#include <net.h>
>> +#include <net6.h>
>> +#include "ndisc.h"
>> +
>> +static ushort seq_no;
>> +
>> +/* the ipv6 address to ping */
>> +struct in6_addr net_ping_ip6;
>> +
>> +int
>> +ip6_make_ping(uchar *eth_dst_addr, struct in6_addr *neigh_addr, uchar *pkt)
>> +{
>> +       struct echo_msg *msg;
>> +       __u16 len;
>> +       uchar *pkt_old = pkt;
>> +
>> +       len = sizeof(struct echo_msg);
>> +
>> +       pkt += net_set_ether(pkt, eth_dst_addr, PROT_IP6);
>> +       pkt += ip6_add_hdr(pkt, &net_ip6, neigh_addr, IPPROTO_ICMPV6,
>> +                          IPV6_NDISC_HOPLIMIT, len);
>> +
>> +       /* ICMPv6 - Echo */
>> +       msg = (struct echo_msg *)pkt;
>> +       msg->icmph.icmp6_type = IPV6_ICMP_ECHO_REQUEST;
>> +       msg->icmph.icmp6_code = 0;
>> +       msg->icmph.icmp6_cksum = 0;
>> +       msg->icmph.icmp6_identifier = 0;
>> +       msg->icmph.icmp6_sequence = htons(seq_no++);
>> +       msg->id = msg->icmph.icmp6_identifier;  /* these seem redundant */
>> +       msg->sequence = msg->icmph.icmp6_sequence;
>> +
>> +       /* checksum */
>> +       msg->icmph.icmp6_cksum = csum_ipv6_magic(&net_ip6, neigh_addr, len,
>> +                                                IPPROTO_ICMPV6,
>> +                                                csum_partial((__u8 *)msg, len, 0));
>> +
>> +       pkt += len;
>> +
>> +       return pkt - pkt_old;
>> +}
>> +
>> +int ping6_send(void)
>> +{
>> +       uchar *pkt;
>> +       static uchar mac[6];
>> +
>> +       /* always send neighbor solicit */
>> +
>> +       memcpy(mac, net_null_ethaddr, 6);
>> +
>> +       net_nd_sol_packet_ip6 = net_ping_ip6;
>> +       net_nd_packet_mac = mac;
>> +
>> +       pkt = net_nd_tx_packet;
>> +       pkt += ip6_make_ping(mac, &net_ping_ip6, pkt);
>> +
>> +       /* size of the waiting packet */
>> +       net_nd_tx_packet_size = (pkt - net_nd_tx_packet);
>> +
>> +       /* and do the ARP request */
>> +       net_nd_try = 1;
>> +       net_nd_timer_start = get_timer(0);
>> +       ip6_NDISC_Request();
>> +       return 1;               /* waiting */
>> +}
>> +
>> +static void ping6_timeout(void)
>> +{
>> +       eth_halt();
>> +       net_set_state(NETLOOP_FAIL);    /* we did not get the reply */
>> +}
>> +
>> +void ping6_start(void)
>> +{
>> +       printf("Using %s device\n", eth_get_name());
>> +       net_set_timeout_handler(10000UL, ping6_timeout);
>> +
>> +       ping6_send();
>> +}
>> +
>> +void ping6_receive(struct ethernet_hdr *et, struct ip6_hdr *ip6, int len)
>> +{
>> +       struct icmp6hdr *icmp =
>> +           (struct icmp6hdr *)(((uchar *)ip6) + IP6_HDR_SIZE);
>> +       struct in6_addr src_ip;
>> +
>> +       switch (icmp->icmp6_type) {
>> +       case IPV6_ICMP_ECHO_REPLY:
>> +               src_ip = ip6->saddr;
>> +               if (memcmp(&net_ping_ip6, &src_ip, sizeof(struct in6_addr)) != 0)
>> +                       return;
>> +               net_set_state(NETLOOP_SUCCESS);
>> +               break;
>> +       case IPV6_ICMP_ECHO_REQUEST:
>> +               debug("Got ICMPv6 ECHO REQUEST from %pI6c\n", &ip6->saddr);
>> +               /* ignore for now.... */
>> +               break;
>> +       default:
>> +               debug("Unexpected ICMPv6 type 0x%x\n", icmp->icmp6_type);
>> +       }
>> +}
>> --
>> 2.5.3
>>
>>
>> _______________________________________________
>> U-Boot mailing list
>> U-Boot at lists.denx.de
>> http://lists.denx.de/mailman/listinfo/u-boot
>>

^ permalink raw reply	[flat|nested] 31+ messages in thread

* [U-Boot] [RFC PATCH 6/8] net: TFTP over IPv6
  2015-11-02 20:43   ` Joe Hershberger
@ 2015-11-03 10:15     ` Chris Packham
  2015-11-03 19:43       ` Joe Hershberger
  2015-11-03 10:41     ` Jean-Pierre Tosoni
  1 sibling, 1 reply; 31+ messages in thread
From: Chris Packham @ 2015-11-03 10:15 UTC (permalink / raw)
  To: u-boot

On Tue, Nov 3, 2015 at 9:43 AM, Joe Hershberger
<joe.hershberger@gmail.com> wrote:
> Hi Chris,
>
> On Mon, Oct 12, 2015 at 2:43 AM, Chris Packham <judge.packham@gmail.com> wrote:
>> Add support for UDP/TFTP over IPv6.
>>
>> Signed-off-by: Chris Packham <judge.packham@gmail.com>
>> ---
>> One problem with the [hostIpAddr:]fileName syntax is that IPv6 addresses
>> contains colons. So tftp_start() would be confused by 'tftpboot6
>> $loadaddr 2001:db8::1:zImage'. It is probably possible to change the
>> parsing to separate the host from the filename by parsing from the end
>> (i.e. use strrchr() instead of strchr()) but then there are error cases
>> that may not be handled correctly (e.g. omitting the filename).
>
> I think we should just change the filename separator for tftp6. How about ','?
>

The other strategy that is often used is to use square brackets to
separate the address from other data e.g. http://[2001:db8::1]:8080/.
Maybe that is a better (or at least more common) approach.

>>  common/cmd_net.c | 13 ++++++++++++
>>  include/net6.h   |  4 ++++
>>  net/net.c        |  3 +++
>>  net/net6.c       | 64 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++
>>  net/tftp.c       | 37 ++++++++++++++++++++++++++++++++
>>  5 files changed, 121 insertions(+)
>>
>> diff --git a/common/cmd_net.c b/common/cmd_net.c
>> index 271f91d..3541599 100644
>> --- a/common/cmd_net.c
>> +++ b/common/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] [bootfilename]"
>> +);
>> +#endif
>> +
>>  #ifdef CONFIG_CMD_TFTPPUT
>>  int do_tftpput(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])
>>  {
>> diff --git a/include/net6.h b/include/net6.h
>> index a0374df..df6d38e 100644
>> --- a/include/net6.h
>> +++ b/include/net6.h
>> @@ -264,6 +264,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 349a18e..9e682b4 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 2315704..f5e272a 100644
>> --- a/net/net6.c
>> +++ b/net/net6.c
>> @@ -322,6 +322,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 + IP6_UDPHDR_SIZE);
>> +       /* checksum */
>> +       udp->udp_xsum = 0;
>> +       udp->udp_xsum = csum_ipv6_magic(&net_ip6, dest, len + IP6_UDPHDR_SIZE,
>> +               IPPROTO_UDP, csum_partial((__u8 *)udp, len + IP6_UDPHDR_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 + IP6_UDPHDR_SIZE);
>> +               memcpy(pkt, (uchar *)udp, len + IP6_UDPHDR_SIZE);
>> +
>> +               /* size of the waiting packet */
>> +               net_nd_tx_packet_size = (pkt - net_nd_tx_packet) + IP6_UDPHDR_SIZE + len;
>> +
>> +               /* and do the neighbor solicitation */
>> +               net_nd_try = 1;
>> +               net_nd_timer_start = get_timer(0);
>> +               ip6_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 + IP6_UDPHDR_SIZE);
>> +       (void) eth_send(net_tx_packet, (pkt - net_tx_packet) + IP6_UDPHDR_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 };
>> @@ -368,6 +412,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 +
>> +                                       IP6_UDPHDR_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 1a51131..1463bf2 100644
>> --- a/net/tftp.c
>> +++ b/net/tftp.c
>> @@ -10,6 +10,7 @@
>>  #include <command.h>
>>  #include <mapmem.h>
>>  #include <net.h>
>> +#include <net6.h>
>>  #include <net/tftp.h>
>>  #include "bootp.h"
>>  #ifdef CONFIG_SYS_DIRECT_FLASH_TFTP
>> @@ -94,6 +95,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
>> @@ -129,6 +134,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;
>> @@ -341,6 +348,11 @@ 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 + IP6_UDPHDR_SIZE;
>> +       else
>> +#endif
>>         pkt = net_tx_packet + net_eth_hdr_size() + IP_UDP_HDR_SIZE;
>>
>>         switch (tftp_state) {
>> @@ -440,6 +452,12 @@ static void tftp_send(void)
>>                 break;
>>         }
>>
>> +#ifdef CONFIG_CMD_NET6
>> +       if (tftp6_active)
>> +               net_send_udp_packet6(net_server_ethaddr, &net_server_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);
>>  }
>> @@ -747,6 +765,16 @@ void tftp_start(enum proto_t protocol)
>>         }
>>
>>         printf("Using %s device\n", eth_get_name());
>> +#ifdef CONFIG_CMD_NET6
>> +       tftp6_active = (protocol == TFTP6);
>> +       if (tftp6_active) {
>> +               printf("TFTP from server %pI6c; our IP address is %pI6c",
>> +                      &net_server_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",
>> @@ -756,6 +784,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, &net_server_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.5.3
>>
>> _______________________________________________
>> U-Boot mailing list
>> U-Boot at lists.denx.de
>> http://lists.denx.de/mailman/listinfo/u-boot

^ permalink raw reply	[flat|nested] 31+ messages in thread

* [U-Boot] [RFC PATCH 6/8] net: TFTP over IPv6
  2015-11-02 20:43   ` Joe Hershberger
  2015-11-03 10:15     ` Chris Packham
@ 2015-11-03 10:41     ` Jean-Pierre Tosoni
  2015-11-03 19:44       ` Joe Hershberger
  1 sibling, 1 reply; 31+ messages in thread
From: Jean-Pierre Tosoni @ 2015-11-03 10:41 UTC (permalink / raw)
  To: u-boot

Hi all,

> -----Message d'origine-----
> De : Joe Hershberger [mailto:joe.hershberger at gmail.com]
> Envoy? : lundi 2 novembre 2015 21:44
> ? : Chris Packham
> Cc : u-boot; Joe Hershberger; jp.tosoni at acksys.fr; Hanna Hawa; Angga
> Objet : Re: [U-Boot] [RFC PATCH 6/8] net: TFTP over IPv6
> 
(...)
> > ---
> > One problem with the [hostIpAddr:]fileName syntax is that IPv6
> > addresses contains colons. So tftp_start() would be confused by
> > 'tftpboot6 $loadaddr 2001:db8::1:zImage'. It is probably possible to
> > change the parsing to separate the host from the filename by parsing
> > from the end (i.e. use strrchr() instead of strchr()) but then there
> > are error cases that may not be handled correctly (e.g. omitting the
> filename).
> 
> I think we should just change the filename separator for tftp6. How about
> ','?
> 

How about a '/' to make it look like an URL ? More intuitive maybe.

(...)

^ permalink raw reply	[flat|nested] 31+ messages in thread

* [U-Boot] [RFC PATCH 5/8] net: ipv6 support
  2015-11-03 10:11     ` Chris Packham
@ 2015-11-03 19:41       ` Joe Hershberger
  2015-11-03 20:54         ` Chris Packham
  0 siblings, 1 reply; 31+ messages in thread
From: Joe Hershberger @ 2015-11-03 19:41 UTC (permalink / raw)
  To: u-boot

Hi Chris,

On Tue, Nov 3, 2015 at 4:11 AM, Chris Packham <judge.packham@gmail.com> wrote:
> Hi Joe,
>
> I've answered a few questions below. I'll address your comments more
> completely before sending another round next week (or I can sit on it
> for longer if you want me to give you some breathing room).

Feel free to send it when you're ready.

> On Tue, Nov 3, 2015 at 9:43 AM, Joe Hershberger
> <joe.hershberger@gmail.com> wrote:
>> Hi Chris,
>>
>> On Mon, Oct 12, 2015 at 2:43 AM, Chris Packham <judge.packham@gmail.com> wrote:
>>> Adds basic support for IPv6. Neighbor discovery and ping6 are the only
>>> things supported at the moment.
>>>
>>> Helped-by: Hanna Hawa <hannah@marvell.com> [endian & alignment fixes]
>>> Signed-off-by: Chris Packham <judge.packham@gmail.com>
>>> ---
>>> Now we have something functional. With this you can do something like
>>> 'setenv ipaddr6 3ffe::2' and 'ping6 3ffe::1' should work.
>>>
>>> I seem to have a problem that when you send a ping6 for a non-existent
>>> address that ends up stuck and the next non-ipv6 net operation tries to
>>> resolve it. I suspect this is because the pending neighbor discovery
>>> information isn't cleaned up properly, I need to look into that.
>>>
>>> The environment variable prefixlength6 is a bit fiddly. No-one uses a
>>> netmask6 (it'd mean a lot of ffff:ffff:...) it's almost always done by
>>> including the prefix length in the address (e.g. ip addr add
>>> 2001:db8::1/64) I'm contemplating adopting that syntax and dropping
>>> prefixlength6.
>>>
>>> This patch is bigger than I'd like it to be but I'm not sure how to
>>> split it up and keep the parts build able.
>>
>> It is pretty huge. It's taken me a while to get through it.
>>
>
> Thanks for making the effort to review. I realise it's pretty huge and
> I'll put some effort into splitting it futher. One obvious thing that
> could be split out is the new environment variables.

Sounds good.

>>>  common/Kconfig         |  15 ++
>>>  common/cmd_net.c       |  28 ++++
>>>  include/env_callback.h |   9 ++
>>>  include/env_flags.h    |  10 ++
>>>  include/net.h          |   5 +-
>>>  include/net6.h         | 212 ++++++++++++++++++++++++++++
>>>  net/Kconfig            |   5 +
>>>  net/Makefile           |   3 +
>>>  net/ndisc.c            | 269 +++++++++++++++++++++++++++++++++++
>>>  net/ndisc.h            |  27 ++++
>>>  net/net.c              |  36 ++++-
>>>  net/net6.c             | 375 +++++++++++++++++++++++++++++++++++++++++++++++++
>>>  net/ping6.c            | 111 +++++++++++++++
>>>  13 files changed, 1102 insertions(+), 3 deletions(-)
>>>  create mode 100644 net/ndisc.c
>>>  create mode 100644 net/ndisc.h
>>>  create mode 100644 net/net6.c
>>>  create mode 100644 net/ping6.c
>>>
>>> diff --git a/common/Kconfig b/common/Kconfig
>>> index 2c42b8e..c72563d 100644
>>> --- a/common/Kconfig
>>> +++ b/common/Kconfig
>>> @@ -389,6 +389,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
>>
>> This is added in the next patch, so should probably move there.
>>
>
> Will do.
>
>>> +
>>>  config CMD_TFTPPUT
>>>         bool "tftp put"
>>>         help
>>> @@ -420,6 +429,12 @@ config CMD_PING
>>>         help
>>>           Send ICMP ECHO_REQUEST to network host
>>>
>>> +config CMD_PING6
>>> +       bool "ping6"
>>> +       depends on CMD_NET6
>>> +       help
>>> +         Send ICMPv6 ECHO_REQUEST to network host
>>
>> What makes ping inseparable from the core support?
>>
>
> Mainly testing, I can't test the core code without ping. But I can see
> that from a patch submission point of few splitting it out will make
> review easier.

One simple way to prove out the core without ping would maybe be to
rebase and switch tftp with ping and make sure that tftp works without
ping. Then you at least know you have enough, but you won't know if
you left in more than you need.

>>> +
>>>  config CMD_CDP
>>>         bool "cdp"
>>>         help

--8<---- snip ----8<--

>>> diff --git a/include/env_callback.h b/include/env_callback.h
>>> index 90b95b5..9027f3f 100644
>>> --- a/include/env_callback.h
>>> +++ b/include/env_callback.h
>>> @@ -60,6 +60,14 @@
>>>  #define NET_CALLBACKS
>>>  #endif
>>>
>>> +#ifdef CONFIG_NET6
>>> +#define NET6_CALLBACKS \
>>> +       "ip6addr:ip6addr," \
>>> +       "serverip6:serverip6," \
>>> +       "prefixlength6:prefixlength6,"
>>
>> I like the other nomenclature better as well (included in the address).
>>
>
> I've actually already implemented the code to include the prefixlength
> in the address and it makes things a lot more usable (the parsing code
> is a bit more complicated).

Sounds good.

>>> +#else
>>> +#define NET6_CALLBACKS
>>> +#endif
>>>  /*
>>>   * This list of callback bindings is static, but may be overridden by defining
>>>   * a new association in the ".callbacks" environment variable.
>>> @@ -68,6 +76,7 @@
>>>         ENV_DOT_ESCAPE ENV_FLAGS_VAR ":flags," \
>>>         "baudrate:baudrate," \
>>>         NET_CALLBACKS \
>>> +       NET6_CALLBACKS \
>>>         "loadaddr:loadaddr," \
>>>         SILENT_CALLBACK \
>>>         SPLASHIMAGE_CALLBACK \

--8<---- snip ----8<--

>>> +void ip6_NDISC_Request(void)
>>
>> Please don't use CamelCase looking stuff. All lower case is usually appropriate.
>>
>
> Will fix. The original Allied Telesis implementation was based on a
> fairly old verison and copied the non-standard net.c which was
> recently updated to eliminate camelcase.
>
> Also I was wondering about "ip6_ndisc" vs "ndisc". The latter is
> sorter and kind of fits with "arp" for ipv4 but the difference is that
> unlike arp, neigbor discovery actually operates over ipv6.

I think it's fine to just use ndisc, since it is not going to be
ambiguous, but up to you if you want to change it.

>>> +{
>>> +       if (!ip6_addr_in_subnet(&net_ip6, &net_nd_sol_packet_ip6,
>>> +                               net_prefix_length)) {
>>> +               if (ip6_is_unspecified_addr(&net_gateway6)) {
>>> +                       puts("## Warning: gatewayip6 is needed but not set\n");
>>> +                       net_nd_rep_packet_ip6 = net_nd_sol_packet_ip6;
>>
>> Is this just assuming that since there is no gateway that the device
>> might be on the local segment even though the address is not on our
>> subnet?
>>
>
> Yeah I might need to add some conditions on the configured address not
> being the link local one.

It seems like if your ip is link local you don't want to use the
gateway even if you have one configured.

>>> +               } else {
>>> +                       net_nd_rep_packet_ip6 = net_gateway6;
>>> +               }
>>> +       } else {
>>> +               net_nd_rep_packet_ip6 = net_nd_sol_packet_ip6;
>>> +       }
>>> +
>>> +       ip6_send_ns(&net_nd_rep_packet_ip6);
>>> +}

--8<---- snip ----8<--

>>> +int
>>> +ip6_addr_in_subnet(struct in6_addr *our_addr, struct in6_addr *neigh_addr,
>>> +                  __u32 plen)
>>> +{
>>> +       __be32 *addr_dwords;
>>> +       __be32 *neigh_dwords;
>>> +
>>> +       addr_dwords = our_addr->s6_addr32;
>>> +       neigh_dwords = neigh_addr->s6_addr32;
>>> +
>>> +       while (plen > 32) {
>>> +               if (*addr_dwords++ != *neigh_dwords++)
>>> +                       return 0;
>>> +
>>> +               plen -= 32;
>>> +       }
>>> +
>>> +       /* Check any remaining bits. */
>>> +       if (plen > 0) {
>>> +               /* parameters are in network byte order.
>>> +                  Does this work on a LE host? */
>>
>> So is this still an outstanding question? Probably worth testing on a
>> LE target, but it should work I believe.
>>
>
> I'm actually testing with x86 on QEMU so I think LE is all good. I'll
> remove the comment.

Great. Have you done any testing in sandbox? I'd really like to see
unit tests go in as part of this series.

>>> +               if ((*addr_dwords >> (32 - plen)) !=
>>> +                   (*neigh_dwords >> (32 - plen))) {
>>> +                       return 0;
>>> +               }
>>> +       }
>>> +
>>> +       return 1;
>>> +}

--8<---- snip ----8<--

^ permalink raw reply	[flat|nested] 31+ messages in thread

* [U-Boot] [RFC PATCH 6/8] net: TFTP over IPv6
  2015-11-03 10:15     ` Chris Packham
@ 2015-11-03 19:43       ` Joe Hershberger
  2015-11-04  8:42         ` Chris Packham
  0 siblings, 1 reply; 31+ messages in thread
From: Joe Hershberger @ 2015-11-03 19:43 UTC (permalink / raw)
  To: u-boot

Hi Chris,

On Tue, Nov 3, 2015 at 4:15 AM, Chris Packham <judge.packham@gmail.com> wrote:
> On Tue, Nov 3, 2015 at 9:43 AM, Joe Hershberger
> <joe.hershberger@gmail.com> wrote:
>> Hi Chris,
>>
>> On Mon, Oct 12, 2015 at 2:43 AM, Chris Packham <judge.packham@gmail.com> wrote:
>>> Add support for UDP/TFTP over IPv6.
>>>
>>> Signed-off-by: Chris Packham <judge.packham@gmail.com>
>>> ---
>>> One problem with the [hostIpAddr:]fileName syntax is that IPv6 addresses
>>> contains colons. So tftp_start() would be confused by 'tftpboot6
>>> $loadaddr 2001:db8::1:zImage'. It is probably possible to change the
>>> parsing to separate the host from the filename by parsing from the end
>>> (i.e. use strrchr() instead of strchr()) but then there are error cases
>>> that may not be handled correctly (e.g. omitting the filename).
>>
>> I think we should just change the filename separator for tftp6. How about ','?
>>
>
> The other strategy that is often used is to use square brackets to
> separate the address from other data e.g. http://[2001:db8::1]:8080/.
> Maybe that is a better (or at least more common) approach.

Sounds good to me - I'm all for going with a common approach.

>>>  common/cmd_net.c | 13 ++++++++++++
>>>  include/net6.h   |  4 ++++
>>>  net/net.c        |  3 +++
>>>  net/net6.c       | 64 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++
>>>  net/tftp.c       | 37 ++++++++++++++++++++++++++++++++
>>>  5 files changed, 121 insertions(+)

--8<---- snip ----8<--

^ permalink raw reply	[flat|nested] 31+ messages in thread

* [U-Boot] [RFC PATCH 6/8] net: TFTP over IPv6
  2015-11-03 10:41     ` Jean-Pierre Tosoni
@ 2015-11-03 19:44       ` Joe Hershberger
  0 siblings, 0 replies; 31+ messages in thread
From: Joe Hershberger @ 2015-11-03 19:44 UTC (permalink / raw)
  To: u-boot

Hi Jean-Pierre,

On Tue, Nov 3, 2015 at 4:41 AM, Jean-Pierre Tosoni <jp.tosoni@acksys.fr> wrote:
> Hi all,
>
>> -----Message d'origine-----
>> De : Joe Hershberger [mailto:joe.hershberger at gmail.com]
>> Envoy? : lundi 2 novembre 2015 21:44
>> ? : Chris Packham
>> Cc : u-boot; Joe Hershberger; jp.tosoni at acksys.fr; Hanna Hawa; Angga
>> Objet : Re: [U-Boot] [RFC PATCH 6/8] net: TFTP over IPv6
>>
> (...)
>> > ---
>> > One problem with the [hostIpAddr:]fileName syntax is that IPv6
>> > addresses contains colons. So tftp_start() would be confused by
>> > 'tftpboot6 $loadaddr 2001:db8::1:zImage'. It is probably possible to
>> > change the parsing to separate the host from the filename by parsing
>> > from the end (i.e. use strrchr() instead of strchr()) but then there
>> > are error cases that may not be handled correctly (e.g. omitting the
>> filename).
>>
>> I think we should just change the filename separator for tftp6. How about
>> ','?
>>
>
> How about a '/' to make it look like an URL ? More intuitive maybe.

That might work, but it also could be part of the path to the file
itself. It doesn't seem good to be ambiguous about that or to require
2001:db8::1//kernel.tgz

-Joe

^ permalink raw reply	[flat|nested] 31+ messages in thread

* [U-Boot] [RFC PATCH 5/8] net: ipv6 support
  2015-11-03 19:41       ` Joe Hershberger
@ 2015-11-03 20:54         ` Chris Packham
  2015-11-03 21:03           ` Joe Hershberger
  0 siblings, 1 reply; 31+ messages in thread
From: Chris Packham @ 2015-11-03 20:54 UTC (permalink / raw)
  To: u-boot

On Wed, Nov 4, 2015 at 8:41 AM, Joe Hershberger
<joe.hershberger@gmail.com> wrote:
<snip>

>> I'm actually testing with x86 on QEMU so I think LE is all good. I'll
>> remove the comment.
>
> Great. Have you done any testing in sandbox? I'd really like to see
> unit tests go in as part of this series.
>

I'll take a look. For the parsing etc that should be doable. I think I
may run into problems with the linux network stack getting in the way
for some of the link-local handling. Are there any existing tests for
the networking code?

^ permalink raw reply	[flat|nested] 31+ messages in thread

* [U-Boot] [RFC PATCH 5/8] net: ipv6 support
  2015-11-03 20:54         ` Chris Packham
@ 2015-11-03 21:03           ` Joe Hershberger
  0 siblings, 0 replies; 31+ messages in thread
From: Joe Hershberger @ 2015-11-03 21:03 UTC (permalink / raw)
  To: u-boot

Hi Chris,

On Tue, Nov 3, 2015 at 2:54 PM, Chris Packham <judge.packham@gmail.com> wrote:
> On Wed, Nov 4, 2015 at 8:41 AM, Joe Hershberger
> <joe.hershberger@gmail.com> wrote:
> <snip>
>
>>> I'm actually testing with x86 on QEMU so I think LE is all good. I'll
>>> remove the comment.
>>
>> Great. Have you done any testing in sandbox? I'd really like to see
>> unit tests go in as part of this series.
>>
>
> I'll take a look. For the parsing etc that should be doable. I think I
> may run into problems with the linux network stack getting in the way
> for some of the link-local handling. Are there any existing tests for
> the networking code?

Yes, there is test/dm/eth.c that does some pinging to test out the
Ethernet stack.

Also, sandbox has drivers/net/sandbox-raw.c that allows sandbox to
interact with the host network adapter. See
board/sandbox/README.sandbox

Cheers,
-Joe

^ permalink raw reply	[flat|nested] 31+ messages in thread

* [U-Boot] [RFC PATCH 6/8] net: TFTP over IPv6
  2015-11-03 19:43       ` Joe Hershberger
@ 2015-11-04  8:42         ` Chris Packham
  2015-11-04  8:46           ` Angga
  2015-11-05 21:45           ` Joe Hershberger
  0 siblings, 2 replies; 31+ messages in thread
From: Chris Packham @ 2015-11-04  8:42 UTC (permalink / raw)
  To: u-boot

On Wed, Nov 4, 2015 at 8:43 AM, Joe Hershberger
<joe.hershberger@gmail.com> wrote:
> Hi Chris,
>
> On Tue, Nov 3, 2015 at 4:15 AM, Chris Packham <judge.packham@gmail.com> wrote:
>> On Tue, Nov 3, 2015 at 9:43 AM, Joe Hershberger
>> <joe.hershberger@gmail.com> wrote:
>>> Hi Chris,
>>>
>>> On Mon, Oct 12, 2015 at 2:43 AM, Chris Packham <judge.packham@gmail.com> wrote:
>>>> Add support for UDP/TFTP over IPv6.
>>>>
>>>> Signed-off-by: Chris Packham <judge.packham@gmail.com>
>>>> ---
>>>> One problem with the [hostIpAddr:]fileName syntax is that IPv6 addresses
>>>> contains colons. So tftp_start() would be confused by 'tftpboot6
>>>> $loadaddr 2001:db8::1:zImage'. It is probably possible to change the
>>>> parsing to separate the host from the filename by parsing from the end
>>>> (i.e. use strrchr() instead of strchr()) but then there are error cases
>>>> that may not be handled correctly (e.g. omitting the filename).
>>>
>>> I think we should just change the filename separator for tftp6. How about ','?
>>>
>>
>> The other strategy that is often used is to use square brackets to
>> separate the address from other data e.g. http://[2001:db8::1]:8080/.
>> Maybe that is a better (or at least more common) approach.
>
> Sounds good to me - I'm all for going with a common approach.
>

One wrinkle I've just discovered with this suggestion is the command
line help text. Consider the following

  => help tftpboot
  Usage:
  tftpboot [loadAddress] [[hostIPaddr:]bootfilename]

Now the IPv6 version if the above syntax is supported
  => help tftpboot6
  Usage:
  tftpboot6 [loadAddress] [[[hostIP6addr]:]bootfilename]

How do we convey that the address before the bootfilename is optional
but if it is specified square brackets are required around the
address. In other words

  tftpboot6 (valid)
  tftpboot6 zImage (valid)
  tftpboot6 2001:db8::1:zImage (invalid)
  tftpboot6 [2001:db8::1]:zImage (valid)

I still like the square bracket syntax and I'm still planning on
implementing it but I thought I'd raise this now to give people a
chance to object.

^ permalink raw reply	[flat|nested] 31+ messages in thread

* [U-Boot] [RFC PATCH 6/8] net: TFTP over IPv6
  2015-11-04  8:42         ` Chris Packham
@ 2015-11-04  8:46           ` Angga
  2015-11-05 21:45           ` Joe Hershberger
  1 sibling, 0 replies; 31+ messages in thread
From: Angga @ 2015-11-04  8:46 UTC (permalink / raw)
  To: u-boot

Chris

Have a look at https://www.ietf.org/rfc/rfc2732.txt, might give you some hints.


Kind regards

Angga

-----Original Message-----
From: Chris Packham [mailto:judge.packham at gmail.com] 
Sent: Wednesday, 4 November 2015 9:43 p.m.
To: Joe Hershberger
Cc: u-boot; Joe Hershberger; jp.tosoni at acksys.fr; Hanna Hawa; Angga
Subject: Re: [U-Boot] [RFC PATCH 6/8] net: TFTP over IPv6

On Wed, Nov 4, 2015 at 8:43 AM, Joe Hershberger <joe.hershberger@gmail.com> wrote:
> Hi Chris,
>
> On Tue, Nov 3, 2015 at 4:15 AM, Chris Packham <judge.packham@gmail.com> wrote:
>> On Tue, Nov 3, 2015 at 9:43 AM, Joe Hershberger 
>> <joe.hershberger@gmail.com> wrote:
>>> Hi Chris,
>>>
>>> On Mon, Oct 12, 2015 at 2:43 AM, Chris Packham <judge.packham@gmail.com> wrote:
>>>> Add support for UDP/TFTP over IPv6.
>>>>
>>>> Signed-off-by: Chris Packham <judge.packham@gmail.com>
>>>> ---
>>>> One problem with the [hostIpAddr:]fileName syntax is that IPv6 
>>>> addresses contains colons. So tftp_start() would be confused by 
>>>> 'tftpboot6 $loadaddr 2001:db8::1:zImage'. It is probably possible 
>>>> to change the parsing to separate the host from the filename by 
>>>> parsing from the end (i.e. use strrchr() instead of strchr()) but 
>>>> then there are error cases that may not be handled correctly (e.g. omitting the filename).
>>>
>>> I think we should just change the filename separator for tftp6. How about ','?
>>>
>>
>> The other strategy that is often used is to use square brackets to 
>> separate the address from other data e.g. http://[2001:db8::1]:8080/.
>> Maybe that is a better (or at least more common) approach.
>
> Sounds good to me - I'm all for going with a common approach.
>

One wrinkle I've just discovered with this suggestion is the command line help text. Consider the following

  => help tftpboot
  Usage:
  tftpboot [loadAddress] [[hostIPaddr:]bootfilename]

Now the IPv6 version if the above syntax is supported
  => help tftpboot6
  Usage:
  tftpboot6 [loadAddress] [[[hostIP6addr]:]bootfilename]

How do we convey that the address before the bootfilename is optional but if it is specified square brackets are required around the address. In other words

  tftpboot6 (valid)
  tftpboot6 zImage (valid)
  tftpboot6 2001:db8::1:zImage (invalid)
  tftpboot6 [2001:db8::1]:zImage (valid)

I still like the square bracket syntax and I'm still planning on implementing it but I thought I'd raise this now to give people a chance to object.

^ permalink raw reply	[flat|nested] 31+ messages in thread

* [U-Boot] [RFC PATCH 6/8] net: TFTP over IPv6
  2015-11-04  8:42         ` Chris Packham
  2015-11-04  8:46           ` Angga
@ 2015-11-05 21:45           ` Joe Hershberger
  1 sibling, 0 replies; 31+ messages in thread
From: Joe Hershberger @ 2015-11-05 21:45 UTC (permalink / raw)
  To: u-boot

Hi Chris,

On Wed, Nov 4, 2015 at 2:42 AM, Chris Packham <judge.packham@gmail.com> wrote:
> On Wed, Nov 4, 2015 at 8:43 AM, Joe Hershberger
> <joe.hershberger@gmail.com> wrote:
>> Hi Chris,
>>
>> On Tue, Nov 3, 2015 at 4:15 AM, Chris Packham <judge.packham@gmail.com> wrote:
>>> On Tue, Nov 3, 2015 at 9:43 AM, Joe Hershberger
>>> <joe.hershberger@gmail.com> wrote:
>>>> Hi Chris,
>>>>
>>>> On Mon, Oct 12, 2015 at 2:43 AM, Chris Packham <judge.packham@gmail.com> wrote:
>>>>> Add support for UDP/TFTP over IPv6.
>>>>>
>>>>> Signed-off-by: Chris Packham <judge.packham@gmail.com>
>>>>> ---
>>>>> One problem with the [hostIpAddr:]fileName syntax is that IPv6 addresses
>>>>> contains colons. So tftp_start() would be confused by 'tftpboot6
>>>>> $loadaddr 2001:db8::1:zImage'. It is probably possible to change the
>>>>> parsing to separate the host from the filename by parsing from the end
>>>>> (i.e. use strrchr() instead of strchr()) but then there are error cases
>>>>> that may not be handled correctly (e.g. omitting the filename).
>>>>
>>>> I think we should just change the filename separator for tftp6. How about ','?
>>>>
>>>
>>> The other strategy that is often used is to use square brackets to
>>> separate the address from other data e.g. http://[2001:db8::1]:8080/.
>>> Maybe that is a better (or at least more common) approach.
>>
>> Sounds good to me - I'm all for going with a common approach.
>>
>
> One wrinkle I've just discovered with this suggestion is the command
> line help text. Consider the following
>
>   => help tftpboot
>   Usage:
>   tftpboot [loadAddress] [[hostIPaddr:]bootfilename]
>
> Now the IPv6 version if the above syntax is supported
>   => help tftpboot6
>   Usage:
>   tftpboot6 [loadAddress] [[[hostIP6addr]:]bootfilename]
>
> How do we convey that the address before the bootfilename is optional
> but if it is specified square brackets are required around the
> address. In other words
>
>   tftpboot6 (valid)
>   tftpboot6 zImage (valid)
>   tftpboot6 2001:db8::1:zImage (invalid)
>   tftpboot6 [2001:db8::1]:zImage (valid)
>
> I still like the square bracket syntax and I'm still planning on
> implementing it but I thought I'd raise this now to give people a
> chance to object.

That's unfortunate.

Maybe something like

tftpboot6 [loadAddress] [[\[hostIP6addr\]:]bootfilename]

or

tftpboot6 [loadAddress] [['['hostIP6addr']':]bootfilename]

I don't love either, but does it get the message across?

-Joe

^ permalink raw reply	[flat|nested] 31+ messages in thread

* [U-Boot] [RFC PATCH 3/8] lib: net_utils: make string_to_ip stricter
  2015-10-12  7:43 ` [U-Boot] [RFC PATCH 3/8] lib: net_utils: make string_to_ip stricter Chris Packham
  2015-11-02 20:42   ` Joe Hershberger
@ 2015-11-09  2:49   ` Chris Packham
  1 sibling, 0 replies; 31+ messages in thread
From: Chris Packham @ 2015-11-09  2:49 UTC (permalink / raw)
  To: u-boot

On Mon, Oct 12, 2015 at 8:43 PM, Chris Packham <judge.packham@gmail.com> wrote:
> Previously values greater than 255 were implicitly truncated. Add some
> stricter checking to reject addresses with components >255.
>
> With the input "1234192.168.1.1" the old behaviour would truncate the
> address to 192.168.1.1. New behaviour rejects the string outright and
> returns 0.0.0.0, which for the purposes of IP addresses can be
> considered an error.
>
> Signed-off-by: Chris Packham <judge.packham@gmail.com>
> ---
>
>  lib/net_utils.c | 13 +++++++++++--
>  1 file changed, 11 insertions(+), 2 deletions(-)
>
> diff --git a/lib/net_utils.c b/lib/net_utils.c
> index cfae842..0fca54d 100644
> --- a/lib/net_utils.c
> +++ b/lib/net_utils.c
> @@ -24,10 +24,19 @@ struct in_addr string_to_ip(const char *s)
>
>         for (addr.s_addr = 0, i = 0; i < 4; ++i) {
>                 ulong val = s ? simple_strtoul(s, &e, 10) : 0;
> +               if (val > 255) {
> +                       addr.s_addr = 0;
> +                       return addr;
> +               }
>                 addr.s_addr <<= 8;
>                 addr.s_addr |= (val & 0xFF);
> -               if (s) {
> -                       s = (*e) ? e+1 : e;
> +               if (*e == '.') {
> +                       s = e + 1;
> +               } else if (*e == '\0') {
> +                       break;
> +               } else {
> +                       addr.s_addr = 0;
> +                       return addr;

One unintended side effect of this is that is breaks parsing of things
like 'tftpboot 192.168.1.1:zImage'. I'll fix that in the next round.

>                 }
>         }
>
> --
> 2.5.3
>

^ permalink raw reply	[flat|nested] 31+ messages in thread

end of thread, other threads:[~2015-11-09  2:49 UTC | newest]

Thread overview: 31+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2015-10-12  7:43 [U-Boot] [RFC PATCH 0/8] IPv6 support Chris Packham
2015-10-12  7:43 ` [U-Boot] [RFC PATCH 1/8] Initial net6.h Chris Packham
2015-11-02 20:32   ` Joe Hershberger
2015-10-12  7:43 ` [U-Boot] [RFC PATCH 2/8] lib: vsprintf: add IPv6 compressed format %pI6c Chris Packham
2015-11-02 20:42   ` Joe Hershberger
2015-10-12  7:43 ` [U-Boot] [RFC PATCH 3/8] lib: net_utils: make string_to_ip stricter Chris Packham
2015-11-02 20:42   ` Joe Hershberger
2015-11-09  2:49   ` Chris Packham
2015-10-12  7:43 ` [U-Boot] [RFC PATCH 4/8] lib: net_utils: add string_to_ip6 Chris Packham
2015-11-02 20:43   ` Joe Hershberger
2015-10-12  7:43 ` [U-Boot] [RFC PATCH 5/8] net: ipv6 support Chris Packham
2015-11-02 20:43   ` Joe Hershberger
2015-11-03 10:11     ` Chris Packham
2015-11-03 19:41       ` Joe Hershberger
2015-11-03 20:54         ` Chris Packham
2015-11-03 21:03           ` Joe Hershberger
2015-10-12  7:43 ` [U-Boot] [RFC PATCH 6/8] net: TFTP over IPv6 Chris Packham
2015-11-02 20:43   ` Joe Hershberger
2015-11-03 10:15     ` Chris Packham
2015-11-03 19:43       ` Joe Hershberger
2015-11-04  8:42         ` Chris Packham
2015-11-04  8:46           ` Angga
2015-11-05 21:45           ` Joe Hershberger
2015-11-03 10:41     ` Jean-Pierre Tosoni
2015-11-03 19:44       ` Joe Hershberger
2015-10-12  7:43 ` [U-Boot] [RFC PATCH 7/8] net: IPv6 documentation Chris Packham
2015-11-02 20:43   ` Joe Hershberger
2015-10-12  7:43 ` [U-Boot] [RFC PATCH 8/8] net: e1000 enable multicast reception Chris Packham
2015-11-02 20:44   ` Joe Hershberger
2015-10-12  8:36 ` [U-Boot] [RFC PATCH 0/8] IPv6 support Jean-Pierre Tosoni
2015-11-02 20:41 ` Joe Hershberger

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.