[U-Boot] [RFC PATCH v2 00/11] IPv6 support

This series adds basic IPv6 support to U-boot. It is a reboot of my earlier work on this[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.
This is still very much a work in progress but it is functional. If anyone is willing/able to test on some other hardware I'd appreciate some feedback.
If it's helpful for testing I'm keeping the 'ipv6' branch of https://github.com/cpackham/u-boot.git up to date with these changes.
A few open issues
1) 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. It has been suggested that this parameter be removed and any users that care can re-parse the packet.
2) Unit tests. This code needs them. The testing so-far has been ad-hoc manual testing using qemu-x86.
3) Fancy v6 feature xyz. There are a lot of things that _could_ be implemented on top of this (DHCPV6 and SLAAC are two obvious things that stick out). For now I want to concentrate on getting the core code stable and accepted, if anyone else wants to work on either of those features that'd be great.
-- [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.
Changes in v2: - Split environment variables from main implementation - remove "prefixlength6" environment variable. The prefix length is now set when specifying the address i.e. setenv ip6addr 2001:db8::1/64. - split ping6 support into separate patch - split environment variables into separate patch - change ip6_ndisc_* to ndisc_*, fix CamelCase - split ping6 support into it's own patch
Chris Packham (11): 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: add definition of udp_hdr net: IPv6 skeleton and environment variables net: IPv6 support net: Add ping6 command and implementation 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 | 32 ++++ drivers/net/e1000.c | 5 + include/env_callback.h | 8 + include/env_flags.h | 9 + include/net.h | 18 +- include/net6.h | 262 ++++++++++++++++++++++++++++ lib/net_utils.c | 132 +++++++++++++- lib/vsprintf.c | 154 ++++++++++++++--- net/Kconfig | 5 + net/Makefile | 3 + net/ndisc.c | 266 ++++++++++++++++++++++++++++ net/ndisc.h | 25 +++ net/net.c | 41 ++++- net/net6.c | 459 +++++++++++++++++++++++++++++++++++++++++++++++++ net/ping6.c | 111 ++++++++++++ net/tftp.c | 58 +++++++ 19 files changed, 1616 insertions(+), 31 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

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
---
Changes in v2: None
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__ */

Hi Chris,
On Mon, Nov 9, 2015 at 1:38 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
Changes in v2: None
Still looks fine to me.

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 ---
Changes in v2: None
include/net6.h | 13 +++++ lib/vsprintf.c | 154 ++++++++++++++++++++++++++++++++++++++++++++++++--------- 2 files changed, 144 insertions(+), 23 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..2baac47 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,121 @@ 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, +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 *ip4_addr_string(char *buf, char *end, u8 *addr, int field_width, int precision, int flags) { - /* (8 * 4 hex digits), 7 colons and trailing zero */ - char ip6_addr[8 * 5]; - char *p = ip6_addr; + char ip4_addr[sizeof("255.255.255.255")]; + + ip4_string(ip4_addr, addr); + + return string(buf, end, ip4_addr, field_width, precision, + flags & ~SPECIAL); +} +#endif + +#ifdef CONFIG_NET6 +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) +{ int i;
for (i = 0; i < 8; i++) { @@ -485,30 +596,20 @@ static char *ip6_addr_string(char *buf, char *end, u8 *addr, int field_width, } *p = '\0';
- return string(buf, end, ip6_addr, field_width, precision, - flags & ~SPECIAL); + return p; }
-static char *ip4_addr_string(char *buf, char *end, u8 *addr, int field_width, +static char *ip6_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 ip6_addr[sizeof("xxxx:xxxx:xxxx:xxxx:xxxx:xxxx: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'; + if (flags & COMPRESSED) + ip6_compressed_string(ip6_addr, addr); + else + ip6_string(ip6_addr, addr, flags);
- return string(buf, end, ip4_addr, field_width, precision, + return string(buf, end, ip6_addr, field_width, precision, flags & ~SPECIAL); } #endif @@ -526,6 +627,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 +672,14 @@ static char *pointer(const char *fmt, char *buf, char *end, void *ptr, flags |= SPECIAL; /* Fallthrough */ case 'I': - if (fmt[1] == '6') +#ifdef CONFIG_NET6 + if (fmt[1] == '6') { + if (fmt[2] == 'c') + flags |= COMPRESSED; return ip6_addr_string(buf, end, ptr, field_width, precision, flags); + } +#endif if (fmt[1] == '4') return ip4_addr_string(buf, end, ptr, field_width, precision, flags);

On Mon, Nov 9, 2015 at 1:38 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
Changes in v2: None
Still looks fine.

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 ---
Changes in v2: - restore some lazy parsing behavior that the tftpboot command relied on.
lib/net_utils.c | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-)
diff --git a/lib/net_utils.c b/lib/net_utils.c index cfae842..f148b8a 100644 --- a/lib/net_utils.c +++ b/lib/net_utils.c @@ -24,11 +24,16 @@ 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 + break; }
addr.s_addr = htonl(addr.s_addr);

On Mon, Nov 9, 2015 at 1:38 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
Changes in v2:
- restore some lazy parsing behavior that the tftpboot command relied on.
It would be good to explicitly describe (in the change log) what you had to change about your stricter parsing that tftpboot required. It seems your commit log didn't change.
lib/net_utils.c | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-)
diff --git a/lib/net_utils.c b/lib/net_utils.c index cfae842..f148b8a 100644 --- a/lib/net_utils.c +++ b/lib/net_utils.c @@ -24,11 +24,16 @@ 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
break; } addr.s_addr = htonl(addr.s_addr);
-- 2.5.3
U-Boot mailing list U-Boot@lists.denx.de http://lists.denx.de/mailman/listinfo/u-boot

On Tue, Nov 24, 2015 at 2:06 PM, Joe Hershberger joe.hershberger@gmail.com wrote:
On Mon, Nov 9, 2015 at 1:38 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
Changes in v2:
- restore some lazy parsing behavior that the tftpboot command relied on.
It would be good to explicitly describe (in the change log) what you had to change about your stricter parsing that tftpboot required. It seems your commit log didn't change.
Will do. For the record it the issue was that the tftpboot code expected to be able parse the ip address part from '192.168.1.1:zImage'. At some point it might be worth adopting an api like strtoul which gives you a pointer to where the parsing stopped.
lib/net_utils.c | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-)
diff --git a/lib/net_utils.c b/lib/net_utils.c index cfae842..f148b8a 100644 --- a/lib/net_utils.c +++ b/lib/net_utils.c @@ -24,11 +24,16 @@ 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
break; } addr.s_addr = htonl(addr.s_addr);
-- 2.5.3
U-Boot mailing list U-Boot@lists.denx.de http://lists.denx.de/mailman/listinfo/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.
Changes in v2: - Wrap code in CONFIG_NET6
include/net6.h | 3 ++ lib/net_utils.c | 121 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 124 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 f148b8a..7422c27 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) { @@ -39,3 +41,122 @@ struct in_addr string_to_ip(const char *s) addr.s_addr = htonl(addr.s_addr); return addr; } + +#ifdef CONFIG_NET6 +/** + * 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) + 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; +} +#endif

On Mon, Nov 9, 2015 at 1:38 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
This looks fine for now.

UDP is the same over IPv4 as it is over other protocols (i.e. IPv6) add a definition of just the UDP header independent of the IPv4 header that it may or may not be combined with.
Signed-off-by: Chris Packham judge.packham@gmail.com --- Ideally struct ip_udp_hdr would be defined as
struct ip_udp_hdr { struct ip_hdr ip; struct udp_hdr udp; };
Implementing this touches more code that I really want to at this point. Some of the code that currently uses struct ip_udp_hdr could probably just use struct ip_hdr instead but some care would need to be taken to much such a change.
Changes in v2: None
include/net.h | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-)
diff --git a/include/net.h b/include/net.h index ebed29a..e8bd2b7 100644 --- a/include/net.h +++ b/include/net.h @@ -348,6 +348,18 @@ struct ip_hdr { #define IP_HDR_SIZE (sizeof(struct ip_hdr))
/* + * UDP header. + */ +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 UDP_HDR_SIZE (sizeof(struct udp_hdr)) + +/* * Internet Protocol (IP) + UDP header. */ struct ip_udp_hdr { @@ -368,7 +380,6 @@ struct ip_udp_hdr { };
#define IP_UDP_HDR_SIZE (sizeof(struct ip_udp_hdr)) -#define UDP_HDR_SIZE (IP_UDP_HDR_SIZE - IP_HDR_SIZE)
/* * Address Resolution Protocol (ARP) header.

On Mon, Nov 9, 2015 at 1:38 AM, Chris Packham judge.packham@gmail.com wrote:
UDP is the same over IPv4 as it is over other protocols (i.e. IPv6) add a definition of just the UDP header independent of the IPv4 header that it may or may not be combined with.
Signed-off-by: Chris Packham judge.packham@gmail.com
Ideally struct ip_udp_hdr would be defined as
struct ip_udp_hdr { struct ip_hdr ip; struct udp_hdr udp; };
Implementing this touches more code that I really want to at this point. Some of the code that currently uses struct ip_udp_hdr could probably just use struct ip_hdr instead but some care would need to be taken to much such a change.
Sounds reasonable.
Changes in v2: None
include/net.h | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-)
diff --git a/include/net.h b/include/net.h index ebed29a..e8bd2b7 100644 --- a/include/net.h +++ b/include/net.h @@ -348,6 +348,18 @@ struct ip_hdr { #define IP_HDR_SIZE (sizeof(struct ip_hdr))
/*
UDP header.
- */
+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 UDP_HDR_SIZE (sizeof(struct udp_hdr))
+/*
Internet Protocol (IP) + UDP header.
*/ struct ip_udp_hdr { @@ -368,7 +380,6 @@ struct ip_udp_hdr { };
#define IP_UDP_HDR_SIZE (sizeof(struct ip_udp_hdr)) -#define UDP_HDR_SIZE (IP_UDP_HDR_SIZE - IP_HDR_SIZE)
/*
Address Resolution Protocol (ARP) header.
-- 2.5.3
U-Boot mailing list U-Boot@lists.denx.de http://lists.denx.de/mailman/listinfo/u-boot

Create net6.c and add CONFIG_NET6 to Kconfig/Makefile. Also add support for the following environment variables: - ip6addr - gateway6 - serverip6
Signed-off-by: Chris Packham judge.packham@gmail.com ---
Changes in v2: - Split environment variables from main implementation - remove "prefixlength6" environment variable. The prefix length is now set when specifying the address i.e. setenv ip6addr 2001:db8::1/64.
include/env_callback.h | 8 +++++ include/env_flags.h | 9 +++++ net/Kconfig | 5 +++ net/Makefile | 1 + net/net6.c | 89 ++++++++++++++++++++++++++++++++++++++++++++++++++ 5 files changed, 112 insertions(+) create mode 100644 net/net6.c
diff --git a/include/env_callback.h b/include/env_callback.h index 90b95b5..97e245b 100644 --- a/include/env_callback.h +++ b/include/env_callback.h @@ -60,6 +60,13 @@ #define NET_CALLBACKS #endif
+#ifdef CONFIG_NET6 +#define NET6_CALLBACKS \ + "ip6addr:ip6addr," \ + "serverip6:serverip6," +#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 +75,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..bf93f15 100644 --- a/include/env_flags.h +++ b/include/env_flags.h @@ -65,6 +65,14 @@ enum env_flags_varaccess { #define NET_FLAGS #endif
+#ifdef CONFIG_NET6 +#define NET6_FLAGS \ + "ip6addr:s," \ + "serverip6:s," +#else +#define NET6_FLAGS +#endif + #ifndef CONFIG_ENV_OVERWRITE #define SERIAL_FLAGS "serial#:so," #else @@ -74,6 +82,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/net/Kconfig b/net/Kconfig index a44a783..bacd914 100644 --- a/net/Kconfig +++ b/net/Kconfig @@ -32,4 +32,9 @@ config NET_TFTP_VARS If unset, timeout and maximum are hard-defined as 1 second and 10 timouts per TFTP transfer.
+config NET6 + bool "IPv6 support" + help + Support for IPv6 + endif # if NET diff --git a/net/Makefile b/net/Makefile index e9cc8ad..6da8019 100644 --- a/net/Makefile +++ b/net/Makefile @@ -20,3 +20,4 @@ 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_NET6) += net6.o diff --git a/net/net6.c b/net/net6.c new file mode 100644 index 0000000..f778cef --- /dev/null +++ b/net/net6.c @@ -0,0 +1,89 @@ +/* + * 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+ + */ + +#include <common.h> +#include <environment.h> +#include <malloc.h> +#include <net.h> +#include <net6.h> +#include "ndisc.h" + +/* 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; +/* 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) +{ + char *v, *s, *strcopy; + int i; + + if (flags & H_PROGRAMMATIC) + return 0; + + if (op == env_op_delete) { + net_prefix_length = 0; + net_copy_ip6(&net_ip6, &net_null_addr_ip6); + return 0; + } + + strcopy = strdup(value); + if (strcopy == NULL) + return -1; + + net_prefix_length = 128; + i = 0; + s = strcopy; + while (s) { + v = strsep(&s, "/"); + if (!v) + break; + + switch (i++) { + case 0: + string_to_ip6(v, &net_ip6); + break; + case 1: + net_prefix_length = simple_strtoul(v, NULL, 10); + break; + default: + break; + } + } + free(strcopy); + + return 0; +} +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_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);

Hi Chris,
On Mon, Nov 9, 2015 at 1:38 AM, Chris Packham judge.packham@gmail.com wrote:
Create net6.c and add CONFIG_NET6 to Kconfig/Makefile. Also add support for the following environment variables:
- ip6addr
- gateway6
- serverip6
Signed-off-by: Chris Packham judge.packham@gmail.com
Changes in v2:
- Split environment variables from main implementation
- remove "prefixlength6" environment variable. The prefix length is now set when specifying the address i.e. setenv ip6addr 2001:db8::1/64.
include/env_callback.h | 8 +++++ include/env_flags.h | 9 +++++ net/Kconfig | 5 +++ net/Makefile | 1 + net/net6.c | 89 ++++++++++++++++++++++++++++++++++++++++++++++++++ 5 files changed, 112 insertions(+) create mode 100644 net/net6.c
diff --git a/include/env_callback.h b/include/env_callback.h index 90b95b5..97e245b 100644 --- a/include/env_callback.h +++ b/include/env_callback.h @@ -60,6 +60,13 @@ #define NET_CALLBACKS #endif
+#ifdef CONFIG_NET6 +#define NET6_CALLBACKS \
"ip6addr:ip6addr," \
"serverip6:serverip6,"
Any reason why gateway6 is not in this list? Seems like an oversight since you define a handler for it below.
+#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 +75,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..bf93f15 100644 --- a/include/env_flags.h +++ b/include/env_flags.h @@ -65,6 +65,14 @@ enum env_flags_varaccess { #define NET_FLAGS #endif
+#ifdef CONFIG_NET6 +#define NET6_FLAGS \
"ip6addr:s," \
"serverip6:s,"
+#else +#define NET6_FLAGS +#endif
#ifndef CONFIG_ENV_OVERWRITE #define SERIAL_FLAGS "serial#:so," #else @@ -74,6 +82,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/net/Kconfig b/net/Kconfig index a44a783..bacd914 100644 --- a/net/Kconfig +++ b/net/Kconfig @@ -32,4 +32,9 @@ config NET_TFTP_VARS If unset, timeout and maximum are hard-defined as 1 second and 10 timouts per TFTP transfer.
+config NET6
bool "IPv6 support"
help
Support for IPv6
endif # if NET diff --git a/net/Makefile b/net/Makefile index e9cc8ad..6da8019 100644 --- a/net/Makefile +++ b/net/Makefile @@ -20,3 +20,4 @@ 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_NET6) += net6.o diff --git a/net/net6.c b/net/net6.c new file mode 100644 index 0000000..f778cef --- /dev/null +++ b/net/net6.c @@ -0,0 +1,89 @@ +/*
- 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+
- */
+#include <common.h> +#include <environment.h> +#include <malloc.h> +#include <net.h> +#include <net6.h> +#include "ndisc.h"
+/* 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; +/* 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)
+{
char *v, *s, *strcopy;
int i;
if (flags & H_PROGRAMMATIC)
return 0;
if (op == env_op_delete) {
net_prefix_length = 0;
net_copy_ip6(&net_ip6, &net_null_addr_ip6);
return 0;
}
strcopy = strdup(value);
if (strcopy == NULL)
return -1;
+ return -ENOMEM;
net_prefix_length = 128;
Does this imply that you are allowed to omit this part of the address now? I assume you are not allowed to omit it, hence the proposed code below.
i = 0;
s = strcopy;
--------------------
while (s) {
v = strsep(&s, "/");
if (!v)
break;
switch (i++) {
case 0:
string_to_ip6(v, &net_ip6);
break;
case 1:
net_prefix_length = simple_strtoul(v, NULL, 10);
break;
default:
break;
}
}
free(strcopy);
return 0;
+}
-------------------- This is a bit convoluted.
How about this: + int retval = 0; + + if (s) { + v = strsep(&s, "/"); + if (!v) { + retval = -EINVAL; + goto err_out; + } + + string_to_ip6(v, &net_ip6); + + v = strsep(&s, "/"); + if (!v) { + retval = -EINVAL; + goto err_out; + } + + net_prefix_length = simple_strtoul(v, NULL, 10); + } + err_out: + free(strcopy); + + return retval; +}
It is now linear and will return an error code when parsing fails, which will prevent the variable from being changed.
+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_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);
2.5.3
U-Boot mailing list U-Boot@lists.denx.de http://lists.denx.de/mailman/listinfo/u-boot

On Tue, Nov 24, 2015 at 2:06 PM, Joe Hershberger joe.hershberger@gmail.com wrote:
Hi Chris,
On Mon, Nov 9, 2015 at 1:38 AM, Chris Packham judge.packham@gmail.com wrote:
Create net6.c and add CONFIG_NET6 to Kconfig/Makefile. Also add support for the following environment variables:
- ip6addr
- gateway6
- serverip6
Signed-off-by: Chris Packham judge.packham@gmail.com
Changes in v2:
- Split environment variables from main implementation
- remove "prefixlength6" environment variable. The prefix length is now set when specifying the address i.e. setenv ip6addr 2001:db8::1/64.
include/env_callback.h | 8 +++++ include/env_flags.h | 9 +++++ net/Kconfig | 5 +++ net/Makefile | 1 + net/net6.c | 89 ++++++++++++++++++++++++++++++++++++++++++++++++++ 5 files changed, 112 insertions(+) create mode 100644 net/net6.c
diff --git a/include/env_callback.h b/include/env_callback.h index 90b95b5..97e245b 100644 --- a/include/env_callback.h +++ b/include/env_callback.h @@ -60,6 +60,13 @@ #define NET_CALLBACKS #endif
+#ifdef CONFIG_NET6 +#define NET6_CALLBACKS \
"ip6addr:ip6addr," \
"serverip6:serverip6,"
Any reason why gateway6 is not in this list? Seems like an oversight since you define a handler for it below.
Yep my bad. Just an omission on my part.
+#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 +75,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..bf93f15 100644 --- a/include/env_flags.h +++ b/include/env_flags.h @@ -65,6 +65,14 @@ enum env_flags_varaccess { #define NET_FLAGS #endif
+#ifdef CONFIG_NET6 +#define NET6_FLAGS \
"ip6addr:s," \
"serverip6:s,"
+#else +#define NET6_FLAGS +#endif
#ifndef CONFIG_ENV_OVERWRITE #define SERIAL_FLAGS "serial#:so," #else @@ -74,6 +82,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/net/Kconfig b/net/Kconfig index a44a783..bacd914 100644 --- a/net/Kconfig +++ b/net/Kconfig @@ -32,4 +32,9 @@ config NET_TFTP_VARS If unset, timeout and maximum are hard-defined as 1 second and 10 timouts per TFTP transfer.
+config NET6
bool "IPv6 support"
help
Support for IPv6
endif # if NET diff --git a/net/Makefile b/net/Makefile index e9cc8ad..6da8019 100644 --- a/net/Makefile +++ b/net/Makefile @@ -20,3 +20,4 @@ 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_NET6) += net6.o diff --git a/net/net6.c b/net/net6.c new file mode 100644 index 0000000..f778cef --- /dev/null +++ b/net/net6.c @@ -0,0 +1,89 @@ +/*
- 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+
- */
+#include <common.h> +#include <environment.h> +#include <malloc.h> +#include <net.h> +#include <net6.h> +#include "ndisc.h"
+/* 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; +/* 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)
+{
char *v, *s, *strcopy;
int i;
if (flags & H_PROGRAMMATIC)
return 0;
if (op == env_op_delete) {
net_prefix_length = 0;
net_copy_ip6(&net_ip6, &net_null_addr_ip6);
return 0;
}
strcopy = strdup(value);
if (strcopy == NULL)
return -1;
return -ENOMEM;
net_prefix_length = 128;
Does this imply that you are allowed to omit this part of the address now? I assume you are not allowed to omit it, hence the proposed code below.
That was my indention but in practice I think you'd always want to specify a prefix length. Not specifying one is _probably_ a user error.
i = 0;
s = strcopy;
while (s) {
v = strsep(&s, "/");
if (!v)
break;
switch (i++) {
case 0:
string_to_ip6(v, &net_ip6);
break;
case 1:
net_prefix_length = simple_strtoul(v, NULL, 10);
break;
default:
break;
}
}
free(strcopy);
return 0;
+}
This is a bit convoluted.
Agreed.
How about this:
int retval = 0;
if (s) {
v = strsep(&s, "/");
if (!v) {
retval = -EINVAL;
goto err_out;
}
string_to_ip6(v, &net_ip6);
v = strsep(&s, "/");
if (!v) {
retval = -EINVAL;
goto err_out;
}
net_prefix_length = simple_strtoul(v, NULL, 10);
}
err_out:
free(strcopy);
return retval;
+}
It is now linear and will return an error code when parsing fails, which will prevent the variable from being changed.
Looks good to me. I'll work it into the next re-roll of this series.
+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_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);
2.5.3
U-Boot mailing list U-Boot@lists.denx.de http://lists.denx.de/mailman/listinfo/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 and the next patch you can do something like 'setenv ipaddr6 3ffe::1/64' and 'ping6 3ffe::2' 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.
Changes in v2: - split ping6 support into separate patch - split environment variables into separate patch - change ip6_ndisc_* to ndisc_*, fix CamelCase
include/net.h | 1 + include/net6.h | 194 +++++++++++++++++++++++++++++++++++++ net/Makefile | 2 + net/ndisc.c | 266 ++++++++++++++++++++++++++++++++++++++++++++++++++ net/ndisc.h | 25 +++++ net/net.c | 38 +++++++- net/net6.c | 299 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 7 files changed, 823 insertions(+), 2 deletions(-) create mode 100644 net/ndisc.c create mode 100644 net/ndisc.h
diff --git a/include/net.h b/include/net.h index e8bd2b7..8b7c878 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 */ diff --git a/include/net6.h b/include/net6.h index a41eb87..ff97c39 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,145 @@ 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 + +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 +210,49 @@ 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); + +/* 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, const void *from) +{ + memcpy((void *)to, from, sizeof(struct in6_addr)); +} #endif /* __NET6_H__ */ diff --git a/net/Makefile b/net/Makefile index 6da8019..d3534c0 100644 --- a/net/Makefile +++ b/net/Makefile @@ -21,3 +21,5 @@ obj-$(CONFIG_CMD_RARP) += rarp.o obj-$(CONFIG_CMD_SNTP) += sntp.o obj-$(CONFIG_CMD_NET) += tftp.o obj-$(CONFIG_NET6) += net6.o +obj-$(CONFIG_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..290eaee --- /dev/null +++ b/net/ndisc.c @@ -0,0 +1,266 @@ +/* + * net/ndisc.c + * + * (C) Copyright 2013 Allied Telesis Labs NZ + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#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 */ +static 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; +static 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 option into a neighbor discovery packet. + * Returns the number of bytes inserted (which may be >= len) + */ +static int +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 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 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); + 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 - NA */ + 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); + 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 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); +} + +int ndisc_timeout_check(void) +{ + ulong t; + + if (ip6_is_unspecified_addr(&net_nd_sol_packet_ip6)) + return 0; + + 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_set_state(NETLOOP_FAIL); + } else { + net_nd_timer_start = t; + ndisc_request(); + } + } + return 1; +} + +void 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_size = 0; + net_nd_tx_packet = &net_nd_packet_buf[0] + (PKTALIGN - 1); + net_nd_tx_packet -= (ulong)net_nd_tx_packet % PKTALIGN; +} + +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) && + ndisc_has_option(ip6, ND_OPT_SOURCE_LL_ADDR)) { + 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) && + ndisc_has_option(ip6, ND_OPT_TARGET_LL_ADDR)) { + 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..75138d0 --- /dev/null +++ b/net/ndisc.h @@ -0,0 +1,25 @@ +/* + * 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; +/* 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; +/* 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_init(void); +void ndisc_receive(struct ethernet_hdr *et, struct ip6_hdr *ip6, int len); +void ndisc_request(void); +int ndisc_timeout_check(void); diff --git a/net/net.c b/net/net.c index 2926bce..ca24673 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) @@ -341,8 +343,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_NET6 + ip6_make_lladdr(&net_link_local_ip6, net_ethaddr); +#endif + }
return; } @@ -376,6 +382,9 @@ void net_init(void) (i + 1) * PKTSIZE_ALIGN; } arp_init(); +#ifdef CONFIG_NET6 + ndisc_init(); +#endif net_clear_handlers();
/* Only need to setup buffer pointers once. */ @@ -478,6 +487,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 +569,9 @@ restart: if (ctrlc()) { /* cancel any ARP that may not have completed */ net_arp_wait_packet_ip.s_addr = 0; +#ifdef CONFIG_NET6 + net_nd_sol_packet_ip6 = net_null_addr_ip6; +#endif
net_cleanup_loop(); eth_halt(); @@ -570,7 +587,11 @@ restart: }
if (arp_timeout_check() > 0) { - time_start = get_timer(0); + time_start = get_timer(0); +#ifdef CONFIG_NET6 + } else if (ndisc_timeout_check() > 0) { + time_start = get_timer(0); +#endif }
/* @@ -1143,6 +1164,11 @@ void net_process_received_packet(uchar *in_packet, int len) rarp_receive(ip, len); break; #endif +#ifdef CONFIG_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 */ @@ -1297,6 +1323,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 index f778cef..955a089 100644 --- a/net/net6.c +++ b/net/net6.c @@ -8,6 +8,34 @@ * 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 <malloc.h> @@ -15,10 +43,14 @@ #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 */ @@ -87,3 +119,270 @@ static int on_serverip6(const char *name, const char *value, enum env_op op, 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) { + 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; +} + +/* + * Compute checksum of IPv6 "psuedo-header" per RFC2460 section 8.1 + */ +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) { + case IPV6_NDISC_NEIGHBOUR_SOLICITATION: + case IPV6_NDISC_NEIGHBOUR_ADVERTISEMENT: + ndisc_receive(et, ip6, len); + break; + + default: + return; + break; + } + break; + + default: + return; + break; + } +}

On Mon, Nov 9, 2015 at 1:38 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.
ping6 is not supported at this moment... not until the next 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 and the next patch you can do something like 'setenv ipaddr6 3ffe::1/64' and 'ping6 3ffe::2' 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.
Changes in v2:
- split ping6 support into separate patch
- split environment variables into separate patch
- change ip6_ndisc_* to ndisc_*, fix CamelCase
include/net.h | 1 + include/net6.h | 194 +++++++++++++++++++++++++++++++++++++ net/Makefile | 2 + net/ndisc.c | 266 ++++++++++++++++++++++++++++++++++++++++++++++++++ net/ndisc.h | 25 +++++ net/net.c | 38 +++++++- net/net6.c | 299 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 7 files changed, 823 insertions(+), 2 deletions(-) create mode 100644 net/ndisc.c create mode 100644 net/ndisc.h
diff --git a/include/net.h b/include/net.h index e8bd2b7..8b7c878 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 */ diff --git a/include/net6.h b/include/net6.h index a41eb87..ff97c39 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,145 @@ 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. */
you -> your
+#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
+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
This should be in the ping patch.
/* ::ffff:0:0/96 is reserved for v4 mapped addresses */ static inline int ipv6_addr_v4mapped(const struct in6_addr *a) { @@ -61,4 +210,49 @@ 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);
+/* sends an IPv6 echo request to a host */ +int ping6_send(void);
There's no reason for this to be in a header, right? It's only called by ping6_start() from what I can see.
+/* 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);
These ping functions should be in the ping6 patch.
+/* 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, const void *from) +{
memcpy((void *)to, from, sizeof(struct in6_addr));
+} #endif /* __NET6_H__ */ diff --git a/net/Makefile b/net/Makefile index 6da8019..d3534c0 100644 --- a/net/Makefile +++ b/net/Makefile @@ -21,3 +21,5 @@ obj-$(CONFIG_CMD_RARP) += rarp.o obj-$(CONFIG_CMD_SNTP) += sntp.o obj-$(CONFIG_CMD_NET) += tftp.o obj-$(CONFIG_NET6) += net6.o +obj-$(CONFIG_NET6) += ndisc.o +obj-$(CONFIG_CMD_PING6) += ping6.o
This should be in the ping patch.
diff --git a/net/ndisc.c b/net/ndisc.c new file mode 100644 index 0000000..290eaee --- /dev/null +++ b/net/ndisc.c @@ -0,0 +1,266 @@ +/*
- net/ndisc.c
- (C) Copyright 2013 Allied Telesis Labs NZ
- SPDX-License-Identifier: GPL-2.0+
- */
+#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 */ +static 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; +static 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 option into a neighbor discovery packet.
- Returns the number of bytes inserted (which may be >= len)
- */
+static int +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 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 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);
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 - NA */
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);
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 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);
+}
+int ndisc_timeout_check(void) +{
ulong t;
if (ip6_is_unspecified_addr(&net_nd_sol_packet_ip6))
return 0;
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_set_state(NETLOOP_FAIL);
} else {
net_nd_timer_start = t;
ndisc_request();
}
}
return 1;
+}
+void 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_size = 0;
net_nd_tx_packet = &net_nd_packet_buf[0] + (PKTALIGN - 1);
net_nd_tx_packet -= (ulong)net_nd_tx_packet % PKTALIGN;
+}
+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) &&
ndisc_has_option(ip6, ND_OPT_SOURCE_LL_ADDR)) {
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) &&
ndisc_has_option(ip6, ND_OPT_TARGET_LL_ADDR)) {
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..75138d0 --- /dev/null +++ b/net/ndisc.h @@ -0,0 +1,25 @@ +/*
- 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; +/* 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; +/* 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_init(void); +void ndisc_receive(struct ethernet_hdr *et, struct ip6_hdr *ip6, int len); +void ndisc_request(void); +int ndisc_timeout_check(void); diff --git a/net/net.c b/net/net.c index 2926bce..ca24673 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) @@ -341,8 +343,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_NET6
ip6_make_lladdr(&net_link_local_ip6, net_ethaddr);
+#endif
} return;
} @@ -376,6 +382,9 @@ void net_init(void) (i + 1) * PKTSIZE_ALIGN; } arp_init(); +#ifdef CONFIG_NET6
ndisc_init();
+#endif net_clear_handlers();
/* Only need to setup buffer pointers once. */
@@ -478,6 +487,11 @@ restart: ping_start(); break; #endif +#ifdef CONFIG_CMD_PING6
case PING6:
ping6_start();
break;
+#endif
This should be in the ping6 patch.
#if defined(CONFIG_CMD_NFS) case NFS: nfs_start(); @@ -555,6 +569,9 @@ restart: if (ctrlc()) { /* cancel any ARP that may not have completed */ net_arp_wait_packet_ip.s_addr = 0; +#ifdef CONFIG_NET6
net_nd_sol_packet_ip6 = net_null_addr_ip6;
+#endif
net_cleanup_loop(); eth_halt();
@@ -570,7 +587,11 @@ restart: }
if (arp_timeout_check() > 0) {
time_start = get_timer(0);
time_start = get_timer(0);
+#ifdef CONFIG_NET6
} else if (ndisc_timeout_check() > 0) {
time_start = get_timer(0);
+#endif }
/*
@@ -1143,6 +1164,11 @@ void net_process_received_packet(uchar *in_packet, int len) rarp_receive(ip, len); break; #endif +#ifdef CONFIG_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 */ @@ -1297,6 +1323,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
This should be in the ping6 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 index f778cef..955a089 100644 --- a/net/net6.c +++ b/net/net6.c @@ -8,6 +8,34 @@
- SPDX-License-Identifier: GPL-2.0+
*/
+/*
- General Desription:
- The user interface supports commands for TFTP6.
This comment should be in the tftp6 patch.
- 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
------------------------------ This comment should be in the tftp6 patch.
- */
+#define DEBUG
Make sure this is gone for the next version.
#include <common.h> #include <environment.h> #include <malloc.h> @@ -15,10 +43,14 @@ #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 */ @@ -87,3 +119,270 @@ static int on_serverip6(const char *name, const char *value, enum env_op op, 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;
+}
Please add a documentation header for this.
+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 */
+}
Please add a documentation header for this.
+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 generate -> address, generate
- 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
address generate -> address, generate
- 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) {
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;
+}
+/*
- Compute checksum of IPv6 "psuedo-header" per RFC2460 section 8.1
- */
+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) {
case IPV6_NDISC_NEIGHBOUR_SOLICITATION:
case IPV6_NDISC_NEIGHBOUR_ADVERTISEMENT:
ndisc_receive(et, ip6, len);
break;
default:
return;
break;
}
break;
default:
return;
break;
}
+}
2.5.3
U-Boot mailing list U-Boot@lists.denx.de http://lists.denx.de/mailman/listinfo/u-boot

Signed-off-by: Chris Packham judge.packham@gmail.com
---
Changes in v2: - split ping6 support into it's own patch
common/Kconfig | 6 +++ common/cmd_net.c | 28 ++++++++++++++ include/net.h | 4 +- net/net6.c | 7 ++++ net/ping6.c | 111 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ 5 files changed, 154 insertions(+), 2 deletions(-) create mode 100644 net/ping6.c
diff --git a/common/Kconfig b/common/Kconfig index 0388a6c..b1effc6 100644 --- a/common/Kconfig +++ b/common/Kconfig @@ -431,6 +431,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/net.h b/include/net.h index 8b7c878..6a9832c 100644 --- a/include/net.h +++ b/include/net.h @@ -524,8 +524,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, LINKLOCAL };
extern char net_boot_file_name[1024];/* Boot File name */ diff --git a/net/net6.c b/net/net6.c index 955a089..8f0c721 100644 --- a/net/net6.c +++ b/net/net6.c @@ -370,6 +370,13 @@ void net_ip6_handler(struct ethernet_hdr *et, struct ip6_hdr *ip6, int len) 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); diff --git a/net/ping6.c b/net/ping6.c new file mode 100644 index 0000000..34796c6 --- /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); + 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); + } +}

On Mon, Nov 9, 2015 at 1:38 AM, Chris Packham judge.packham@gmail.com wrote:
Signed-off-by: Chris Packham judge.packham@gmail.com
Changes in v2:
- split ping6 support into it's own patch
common/Kconfig | 6 +++ common/cmd_net.c | 28 ++++++++++++++ include/net.h | 4 +- net/net6.c | 7 ++++ net/ping6.c | 111 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ 5 files changed, 154 insertions(+), 2 deletions(-) create mode 100644 net/ping6.c
diff --git a/common/Kconfig b/common/Kconfig index 0388a6c..b1effc6 100644 --- a/common/Kconfig +++ b/common/Kconfig @@ -431,6 +431,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/net.h b/include/net.h index 8b7c878..6a9832c 100644 --- a/include/net.h +++ b/include/net.h @@ -524,8 +524,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, LINKLOCAL
};
extern char net_boot_file_name[1024];/* Boot File name */ diff --git a/net/net6.c b/net/net6.c index 955a089..8f0c721 100644 --- a/net/net6.c +++ b/net/net6.c @@ -370,6 +370,13 @@ void net_ip6_handler(struct ethernet_hdr *et, struct ip6_hdr *ip6, int len) 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);
diff --git a/net/ping6.c b/net/ping6.c new file mode 100644 index 0000000..34796c6 --- /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
+ static 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)
+static 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);
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@lists.denx.de http://lists.denx.de/mailman/listinfo/u-boot

Add support for UDP/TFTP over IPv6. To support specifying an server IPv6 address in the command square brackets must be used to separate the address from the filename. e.g tftpboot6 [2001:db8::1]:zImage
Signed-off-by: Chris Packham judge.packham@gmail.com ---
Changes in v2: None
common/Kconfig | 9 ++++++++ common/cmd_net.c | 13 ++++++++++++ include/net.h | 2 +- include/net6.h | 4 ++++ net/net.c | 3 +++ net/net6.c | 64 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++ net/tftp.c | 58 ++++++++++++++++++++++++++++++++++++++++++++++++++ 7 files changed, 152 insertions(+), 1 deletion(-)
diff --git a/common/Kconfig b/common/Kconfig index b1effc6..5914328 100644 --- a/common/Kconfig +++ b/common/Kconfig @@ -400,6 +400,15 @@ config CMD_NET bootp - boot image via network using BOOTP/TFTP protocol tftpboot - boot image via network using TFTP protocol
+config CMD_NET6 + bool "ipv6 commands" + select NET + select NET6 + default n + help + IPv6 network commands + tftpboot6 - boot image via network using TFTP protocol + config CMD_TFTPPUT bool "tftp put" help diff --git a/common/cmd_net.c b/common/cmd_net.c index 271f91d..47b56ee 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] [[hostIP6Addr]:][bootfilename]" +); +#endif + #ifdef CONFIG_CMD_TFTPPUT int do_tftpput(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) { diff --git a/include/net.h b/include/net.h index 6a9832c..69e6a17 100644 --- a/include/net.h +++ b/include/net.h @@ -525,7 +525,7 @@ extern int net_restart_wrap; /* Tried all network devices */
enum proto_t { BOOTP, RARP, ARP, TFTPGET, DHCP, PING, PING6, DNS, NFS, CDP, NETCONS, - SNTP, TFTPSRV, TFTPPUT, LINKLOCAL + SNTP, TFTPSRV, TFTPPUT, TFTP6, LINKLOCAL };
extern char net_boot_file_name[1024];/* Boot File name */ diff --git a/include/net6.h b/include/net6.h index ff97c39..7ae1777 100644 --- a/include/net6.h +++ b/include/net6.h @@ -246,6 +246,10 @@ void ping6_start(void); void ping6_receive(struct ethernet_hdr *et, struct ip6_hdr *ip6, int len);
+/* Transmit UDP packet using IPv6, performing neighbour discovery if needed */ +int net_send_udp_packet6(uchar *ether, struct in6_addr *dest, + int dport, int sport, int len); + /* handler for incoming IPv6 echo packet */ void net_ip6_handler(struct ethernet_hdr *et, struct ip6_hdr *ip6, int len); diff --git a/net/net.c b/net/net.c index ca24673..2b0d17e 100644 --- a/net/net.c +++ b/net/net.c @@ -453,6 +453,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 8f0c721..5b8a003 100644 --- a/net/net6.c +++ b/net/net6.c @@ -342,6 +342,50 @@ ip6_add_hdr(uchar *xip, struct in6_addr *src, struct in6_addr *dest, return sizeof(struct ip6_hdr); }
+int +net_send_udp_packet6(uchar *ether, struct in6_addr *dest, int dport, int sport, int len) +{ + uchar *pkt; + struct udp_hdr *udp; + + udp = (struct udp_hdr *)((uchar *)net_tx_packet + net_eth_hdr_size() + IP6_HDR_SIZE); + + udp->udp_dst = htons(dport); + udp->udp_src = htons(sport); + udp->udp_len = htons(len + UDP_HDR_SIZE); + /* checksum */ + udp->udp_xsum = 0; + udp->udp_xsum = csum_ipv6_magic(&net_ip6, dest, len + UDP_HDR_SIZE, + IPPROTO_UDP, csum_partial((__u8 *)udp, len + UDP_HDR_SIZE, 0)); + + /* if MAC address was not discovered yet, save the packet and do neighbour discovery */ + if (memcmp(ether, net_null_ethaddr, 6) == 0) { + net_copy_ip6(&net_nd_sol_packet_ip6, dest); + net_nd_packet_mac = ether; + + pkt = net_nd_tx_packet; + pkt += net_set_ether(pkt, net_nd_packet_mac, PROT_IP6); + pkt += ip6_add_hdr(pkt, &net_ip6, dest, IPPROTO_UDP, 64, len + UDP_HDR_SIZE); + memcpy(pkt, (uchar *)udp, len + UDP_HDR_SIZE); + + /* size of the waiting packet */ + net_nd_tx_packet_size = (pkt - net_nd_tx_packet) + UDP_HDR_SIZE + len; + + /* and do the neighbor solicitation */ + net_nd_try = 1; + net_nd_timer_start = get_timer(0); + ndisc_request(); + return 1; /* waiting */ + } + + pkt = (uchar *)net_tx_packet; + pkt += net_set_ether(pkt, ether, PROT_IP6); + pkt += ip6_add_hdr(pkt, &net_ip6, dest, IPPROTO_UDP, 64, len + UDP_HDR_SIZE); + (void) eth_send(net_tx_packet, (pkt - net_tx_packet) + UDP_HDR_SIZE + len); + + return 0; /* transmitted */ +} + void net_ip6_handler(struct ethernet_hdr *et, struct ip6_hdr *ip6, int len) { struct in_addr zero_ip = {.s_addr = 0 }; @@ -388,6 +432,26 @@ void net_ip6_handler(struct ethernet_hdr *et, struct ip6_hdr *ip6, int len) } break;
+ case IPPROTO_UDP: + udp = (struct udp_hdr *)(((uchar *)ip6) + IP6_HDR_SIZE); + csum = udp->udp_xsum; + hlen = ntohs(ip6->payload_len); + udp->udp_xsum = 0; + /* checksum */ + udp->udp_xsum = csum_ipv6_magic(&ip6->saddr, &ip6->daddr, + hlen, IPPROTO_UDP, csum_partial((__u8 *)udp, hlen, 0)); + if (csum != udp->udp_xsum) + return; + + /* IP header OK. Pass the packet to the current handler. */ + net_get_udp_handler()((uchar *)ip6 + IP6_HDR_SIZE + + UDP_HDR_SIZE, + ntohs(udp->udp_dst), + zero_ip, + ntohs(udp->udp_src), + ntohs(udp->udp_len) - 8); + break; + default: return; break; diff --git a/net/tftp.c b/net/tftp.c index f2889fe..aa9e6e4 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 @@ -66,6 +67,9 @@ enum { };
static struct in_addr tftp_remote_ip; +#ifdef CONFIG_CMD_NET6 +static struct in6_addr tftp_remote_ip6; +#endif /* The UDP port at their end */ static int tftp_remote_port; /* The UDP port at our end */ @@ -94,6 +98,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 +137,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 +351,12 @@ static void tftp_send(void) * We will always be sending some sort of packet, so * cobble together the packet headers now. */ +#ifdef CONFIG_CMD_NET6 + if (tftp6_active) + pkt = net_tx_packet + net_eth_hdr_size() + + IP6_HDR_SIZE + UDP_HDR_SIZE; + else +#endif pkt = net_tx_packet + net_eth_hdr_size() + IP_UDP_HDR_SIZE;
switch (tftp_state) { @@ -440,6 +456,12 @@ static void tftp_send(void) break; }
+#ifdef CONFIG_CMD_NET6 + if (tftp6_active) + net_send_udp_packet6(net_server_ethaddr, &tftp_remote_ip6, + tftp_remote_port, tftp_our_port, len); + else +#endif net_send_udp_packet(net_server_ethaddr, tftp_remote_ip, tftp_remote_port, tftp_our_port, len); } @@ -733,6 +755,10 @@ void tftp_start(enum proto_t protocol) debug("TFTP blocksize = %i, timeout = %ld ms\n", tftp_block_size_option, timeout_ms);
+#ifdef CONFIG_CMD_NET6 + tftp6_active = (protocol == TFTP6); + tftp_remote_ip6 = net_server_ip6; +#endif tftp_remote_ip = net_server_ip; if (net_boot_file_name[0] == '\0') { sprintf(default_filename, "%02X%02X%02X%02X.img", @@ -746,6 +772,20 @@ void tftp_start(enum proto_t protocol)
printf("*** Warning: no boot file name; using '%s'\n", tftp_filename); +#ifdef CONFIG_CMD_NET6 + } else if (tftp6_active) { + char *s, *e; + s = strchr(net_boot_file_name, '['); + e = strchr(net_boot_file_name, ']'); + if (s && e) { + *e++ = 0; + string_to_ip6(s + 1, &tftp_remote_ip6); + strncpy(tftp_filename, e + 1, MAX_LEN); + } else { + strncpy(tftp_filename, net_boot_file_name, MAX_LEN); + tftp_filename[MAX_LEN - 1] = 0; + } +#endif } else { char *p = strchr(net_boot_file_name, ':');
@@ -760,6 +800,15 @@ void tftp_start(enum proto_t protocol) }
printf("Using %s device\n", eth_get_name()); +#ifdef CONFIG_CMD_NET6 + if (tftp6_active) { + printf("TFTP from server %pI6c; our IP address is %pI6c", + &tftp_remote_ip6, + &net_ip6); + if (tftp_block_size_option > TFTP_MTU_BLOCKSIZE6) + tftp_block_size_option = TFTP_MTU_BLOCKSIZE6; + } else +#endif printf("TFTP %s server %pI4; our IP address is %pI4", #ifdef CONFIG_CMD_TFTPPUT protocol == TFTPPUT ? "to" : "from", @@ -769,6 +818,15 @@ void tftp_start(enum proto_t protocol) &tftp_remote_ip, &net_ip);
/* Check if we need to send across this subnet */ +#ifdef CONFIG_CMD_NET6 + if (tftp6_active) { + if (!ip6_addr_in_subnet(&net_ip6, &tftp_remote_ip6, + net_prefix_length)) { + printf("; sending through gateway %pI6c", + &net_gateway6); + } + } else +#endif if (net_gateway.s_addr && net_netmask.s_addr) { struct in_addr our_net; struct in_addr remote_net;

On Mon, Nov 9, 2015 at 1:38 AM, Chris Packham judge.packham@gmail.com wrote:
Add support for UDP/TFTP over IPv6. To support specifying an server IPv6 address in the command square brackets must be used to separate the address from the filename. e.g tftpboot6 [2001:db8::1]:zImage
Signed-off-by: Chris Packham judge.packham@gmail.com
Changes in v2: None
Aren't the square brackets new?
common/Kconfig | 9 ++++++++ common/cmd_net.c | 13 ++++++++++++ include/net.h | 2 +- include/net6.h | 4 ++++ net/net.c | 3 +++ net/net6.c | 64 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++ net/tftp.c | 58 ++++++++++++++++++++++++++++++++++++++++++++++++++ 7 files changed, 152 insertions(+), 1 deletion(-)
diff --git a/common/Kconfig b/common/Kconfig index b1effc6..5914328 100644 --- a/common/Kconfig +++ b/common/Kconfig @@ -400,6 +400,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
Wouldn't ping6 be considered a command? Why is this not added in an earlier patch to allow for ping6?
It seems like ping6 would depend on CMD_NET6. Also, it seems like tftp6 should be behind its own CMD_TFTP6 token, since it surely must be optional. Maybe we want it to be enabled by default, but it should be possible to disable.
I think you'll need to update the documentation patch as a result.
config CMD_TFTPPUT bool "tftp put" help diff --git a/common/cmd_net.c b/common/cmd_net.c index 271f91d..47b56ee 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] [[hostIP6Addr]:][bootfilename]"
+); +#endif
#ifdef CONFIG_CMD_TFTPPUT int do_tftpput(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) { diff --git a/include/net.h b/include/net.h index 6a9832c..69e6a17 100644 --- a/include/net.h +++ b/include/net.h @@ -525,7 +525,7 @@ extern int net_restart_wrap; /* Tried all network devices */
enum proto_t { BOOTP, RARP, ARP, TFTPGET, DHCP, PING, PING6, DNS, NFS, CDP, NETCONS,
SNTP, TFTPSRV, TFTPPUT, LINKLOCAL
SNTP, TFTPSRV, TFTPPUT, TFTP6, LINKLOCAL
};
extern char net_boot_file_name[1024];/* Boot File name */ diff --git a/include/net6.h b/include/net6.h index ff97c39..7ae1777 100644 --- a/include/net6.h +++ b/include/net6.h @@ -246,6 +246,10 @@ void ping6_start(void); void ping6_receive(struct ethernet_hdr *et, struct ip6_hdr *ip6, int len);
+/* Transmit UDP packet using IPv6, performing neighbour discovery if needed */ +int net_send_udp_packet6(uchar *ether, struct in6_addr *dest,
int dport, int sport, int len);
/* handler for incoming IPv6 echo packet */ void net_ip6_handler(struct ethernet_hdr *et, struct ip6_hdr *ip6, int len); diff --git a/net/net.c b/net/net.c index ca24673..2b0d17e 100644 --- a/net/net.c +++ b/net/net.c @@ -453,6 +453,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 8f0c721..5b8a003 100644 --- a/net/net6.c +++ b/net/net6.c @@ -342,6 +342,50 @@ ip6_add_hdr(uchar *xip, struct in6_addr *src, struct in6_addr *dest, return sizeof(struct ip6_hdr); }
+int +net_send_udp_packet6(uchar *ether, struct in6_addr *dest, int dport, int sport, int len) +{
uchar *pkt;
struct udp_hdr *udp;
udp = (struct udp_hdr *)((uchar *)net_tx_packet + net_eth_hdr_size() + IP6_HDR_SIZE);
udp->udp_dst = htons(dport);
udp->udp_src = htons(sport);
udp->udp_len = htons(len + UDP_HDR_SIZE);
/* checksum */
udp->udp_xsum = 0;
udp->udp_xsum = csum_ipv6_magic(&net_ip6, dest, len + UDP_HDR_SIZE,
IPPROTO_UDP, csum_partial((__u8 *)udp, len + UDP_HDR_SIZE, 0));
/* if MAC address was not discovered yet, save the packet and do neighbour discovery */
if (memcmp(ether, net_null_ethaddr, 6) == 0) {
net_copy_ip6(&net_nd_sol_packet_ip6, dest);
net_nd_packet_mac = ether;
pkt = net_nd_tx_packet;
pkt += net_set_ether(pkt, net_nd_packet_mac, PROT_IP6);
pkt += ip6_add_hdr(pkt, &net_ip6, dest, IPPROTO_UDP, 64, len + UDP_HDR_SIZE);
memcpy(pkt, (uchar *)udp, len + UDP_HDR_SIZE);
/* size of the waiting packet */
net_nd_tx_packet_size = (pkt - net_nd_tx_packet) + UDP_HDR_SIZE + len;
/* and do the neighbor solicitation */
net_nd_try = 1;
net_nd_timer_start = get_timer(0);
ndisc_request();
return 1; /* waiting */
}
pkt = (uchar *)net_tx_packet;
pkt += net_set_ether(pkt, ether, PROT_IP6);
pkt += ip6_add_hdr(pkt, &net_ip6, dest, IPPROTO_UDP, 64, len + UDP_HDR_SIZE);
(void) eth_send(net_tx_packet, (pkt - net_tx_packet) + UDP_HDR_SIZE + len);
return 0; /* transmitted */
+}
void net_ip6_handler(struct ethernet_hdr *et, struct ip6_hdr *ip6, int len) { struct in_addr zero_ip = {.s_addr = 0 }; @@ -388,6 +432,26 @@ void net_ip6_handler(struct ethernet_hdr *et, struct ip6_hdr *ip6, int len) } break;
case IPPROTO_UDP:
udp = (struct udp_hdr *)(((uchar *)ip6) + IP6_HDR_SIZE);
csum = udp->udp_xsum;
hlen = ntohs(ip6->payload_len);
udp->udp_xsum = 0;
/* checksum */
udp->udp_xsum = csum_ipv6_magic(&ip6->saddr, &ip6->daddr,
hlen, IPPROTO_UDP, csum_partial((__u8 *)udp, hlen, 0));
if (csum != udp->udp_xsum)
return;
/* IP header OK. Pass the packet to the current handler. */
net_get_udp_handler()((uchar *)ip6 + IP6_HDR_SIZE +
UDP_HDR_SIZE,
ntohs(udp->udp_dst),
zero_ip,
ntohs(udp->udp_src),
ntohs(udp->udp_len) - 8);
break;
default: return; break;
diff --git a/net/tftp.c b/net/tftp.c index f2889fe..aa9e6e4 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 @@ -66,6 +67,9 @@ enum { };
static struct in_addr tftp_remote_ip; +#ifdef CONFIG_CMD_NET6 +static struct in6_addr tftp_remote_ip6; +#endif /* The UDP port at their end */ static int tftp_remote_port; /* The UDP port at our end */ @@ -94,6 +98,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 +137,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 +351,12 @@ static void tftp_send(void) * We will always be sending some sort of packet, so * cobble together the packet headers now. */ +#ifdef CONFIG_CMD_NET6
if (tftp6_active)
pkt = net_tx_packet + net_eth_hdr_size() +
IP6_HDR_SIZE + UDP_HDR_SIZE;
else
+#endif pkt = net_tx_packet + net_eth_hdr_size() + IP_UDP_HDR_SIZE;
switch (tftp_state) {
@@ -440,6 +456,12 @@ static void tftp_send(void) break; }
+#ifdef CONFIG_CMD_NET6
if (tftp6_active)
net_send_udp_packet6(net_server_ethaddr, &tftp_remote_ip6,
tftp_remote_port, tftp_our_port, len);
else
+#endif net_send_udp_packet(net_server_ethaddr, tftp_remote_ip, tftp_remote_port, tftp_our_port, len); } @@ -733,6 +755,10 @@ void tftp_start(enum proto_t protocol) debug("TFTP blocksize = %i, timeout = %ld ms\n", tftp_block_size_option, timeout_ms);
+#ifdef CONFIG_CMD_NET6
tftp6_active = (protocol == TFTP6);
tftp_remote_ip6 = net_server_ip6;
+#endif tftp_remote_ip = net_server_ip; if (net_boot_file_name[0] == '\0') { sprintf(default_filename, "%02X%02X%02X%02X.img", @@ -746,6 +772,20 @@ void tftp_start(enum proto_t protocol)
printf("*** Warning: no boot file name; using '%s'\n", tftp_filename);
+#ifdef CONFIG_CMD_NET6
} else if (tftp6_active) {
char *s, *e;
s = strchr(net_boot_file_name, '[');
e = strchr(net_boot_file_name, ']');
if (s && e) {
*e++ = 0;
string_to_ip6(s + 1, &tftp_remote_ip6);
strncpy(tftp_filename, e + 1, MAX_LEN);
} else {
strncpy(tftp_filename, net_boot_file_name, MAX_LEN);
tftp_filename[MAX_LEN - 1] = 0;
}
+#endif } else { char *p = strchr(net_boot_file_name, ':');
@@ -760,6 +800,15 @@ void tftp_start(enum proto_t protocol) }
printf("Using %s device\n", eth_get_name());
+#ifdef CONFIG_CMD_NET6
if (tftp6_active) {
printf("TFTP from server %pI6c; our IP address is %pI6c",
&tftp_remote_ip6,
&net_ip6);
if (tftp_block_size_option > TFTP_MTU_BLOCKSIZE6)
tftp_block_size_option = TFTP_MTU_BLOCKSIZE6;
} else
+#endif printf("TFTP %s server %pI4; our IP address is %pI4", #ifdef CONFIG_CMD_TFTPPUT protocol == TFTPPUT ? "to" : "from", @@ -769,6 +818,15 @@ void tftp_start(enum proto_t protocol) &tftp_remote_ip, &net_ip);
/* Check if we need to send across this subnet */
+#ifdef CONFIG_CMD_NET6
if (tftp6_active) {
if (!ip6_addr_in_subnet(&net_ip6, &tftp_remote_ip6,
net_prefix_length)) {
printf("; sending through gateway %pI6c",
&net_gateway6);
This seems like it should be a debug() message, not a printf().
}
} 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@lists.denx.de http://lists.denx.de/mailman/listinfo/u-boot

Signed-off-by: Chris Packham judge.packham@gmail.com ---
Changes in v2: None
README | 3 +++ doc/README.ipv6 | 32 ++++++++++++++++++++++++++++++++ 2 files changed, 35 insertions(+) create mode 100644 doc/README.ipv6
diff --git a/README b/README index ef8d437..64b431d 100644 --- a/README +++ b/README @@ -1110,6 +1110,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 @@ -1117,6 +1118,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..0537246 --- /dev/null +++ b/doc/README.ipv6 @@ -0,0 +1,32 @@ +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 +- 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).

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.
Changes in v2: None
drivers/net/e1000.c | 5 +++++ 1 file changed, 5 insertions(+)
diff --git a/drivers/net/e1000.c b/drivers/net/e1000.c index 2ba03ed..d19a9de 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_NET6 + rctl |= E1000_RCTL_MPE; +#endif + E1000_WRITE_REG(hw, RCTL, rctl); }

On Mon, Nov 9, 2015 at 1:38 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
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.
I agree that a new token CONFIG_MCAST should be created.
Changes in v2: None
drivers/net/e1000.c | 5 +++++ 1 file changed, 5 insertions(+)
diff --git a/drivers/net/e1000.c b/drivers/net/e1000.c index 2ba03ed..d19a9de 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_NET6
rctl |= E1000_RCTL_MPE;
+#endif
E1000_WRITE_REG(hw, RCTL, rctl);
}
-- 2.5.3
U-Boot mailing list U-Boot@lists.denx.de http://lists.denx.de/mailman/listinfo/u-boot
participants (2)
-
Chris Packham
-
Joe Hershberger