Merge tag 'sound-2023-01-rc4' of https://source.denx.de/u-boot/custodians/u-boot-efi
Pull request for sound-2023-01-rc4
* Avoid endless loop and amend unit test
* Add man-page for the sound command
* Fix sandbox sound driver
diff --git a/Makefile b/Makefile
index 2d24ac3..de57463 100644
--- a/Makefile
+++ b/Makefile
@@ -3,7 +3,7 @@
VERSION = 2023
PATCHLEVEL = 01
SUBLEVEL =
-EXTRAVERSION = -rc2
+EXTRAVERSION = -rc3
NAME =
# *DOCUMENTATION*
diff --git a/cmd/Kconfig b/cmd/Kconfig
index a4b6fc6..d93731f 100644
--- a/cmd/Kconfig
+++ b/cmd/Kconfig
@@ -1847,6 +1847,13 @@
help
Send ICMP ECHO_REQUEST to network host
+config CMD_PING6
+ bool "ping6"
+ depends on IPV6
+ default y if (CMD_PING && IPV6)
+ help
+ Send ICMPv6 ECHO_REQUEST to network host
+
config CMD_CDP
bool "cdp"
help
diff --git a/cmd/net.c b/cmd/net.c
index f6d9f5e..0e9f200 100644
--- a/cmd/net.c
+++ b/cmd/net.c
@@ -14,6 +14,7 @@
#include <env.h>
#include <image.h>
#include <net.h>
+#include <net6.h>
#include <net/udp.h>
#include <net/sntp.h>
#include <net/ncsi.h>
@@ -45,12 +46,22 @@
return ret;
}
+#if IS_ENABLED(CONFIG_IPV6)
+U_BOOT_CMD(
+ tftpboot, 4, 1, do_tftpb,
+ "boot image via network using TFTP protocol\n"
+ "To use IPv6 add -ipv6 parameter or use IPv6 hostIPaddr framed "
+ "with [] brackets",
+ "[loadAddress] [[hostIPaddr:]bootfilename] [" USE_IP6_CMD_PARAM "]"
+);
+#else
U_BOOT_CMD(
tftpboot, 3, 1, do_tftpb,
"load file via network using TFTP protocol",
"[loadAddress] [[hostIPaddr:]bootfilename]"
);
#endif
+#endif
#ifdef CONFIG_CMD_TFTPPUT
static int do_tftpput(struct cmd_tbl *cmdtp, int flag, int argc,
@@ -306,6 +317,17 @@
if (s != NULL)
image_load_addr = hextoul(s, NULL);
+ if (IS_ENABLED(CONFIG_IPV6)) {
+ use_ip6 = false;
+
+ /* IPv6 parameter has to be always *last* */
+ if (!strcmp(argv[argc - 1], USE_IP6_CMD_PARAM)) {
+ use_ip6 = true;
+ /* It is a hack not to break switch/case code */
+ --argc;
+ }
+ }
+
if (parse_args(proto, argc, argv)) {
bootstage_error(BOOTSTAGE_ID_NET_START);
return CMD_RET_USAGE;
@@ -313,6 +335,19 @@
bootstage_mark(BOOTSTAGE_ID_NET_START);
+ if (IS_ENABLED(CONFIG_IPV6) && !use_ip6) {
+ char *s, *e;
+ size_t len;
+
+ s = strchr(net_boot_file_name, '[');
+ e = strchr(net_boot_file_name, ']');
+ if (s && e) {
+ len = e - s;
+ if (!string_to_ip6(s + 1, len - 1, &net_server_ip6))
+ use_ip6 = true;
+ }
+ }
+
size = net_loop(proto);
if (size < 0) {
bootstage_error(BOOTSTAGE_ID_NET_NETLOOP_OK);
@@ -368,6 +403,32 @@
);
#endif
+#if IS_ENABLED(CONFIG_CMD_PING6)
+int do_ping6(struct cmd_tbl *cmdtp, int flag, int argc, char * const argv[])
+{
+ if (string_to_ip6(argv[1], strlen(argv[1]), &net_ping_ip6))
+ return CMD_RET_USAGE;
+
+ use_ip6 = true;
+ if (net_loop(PING6) < 0) {
+ use_ip6 = false;
+ printf("ping6 failed; host %pI6c is not alive\n",
+ &net_ping_ip6);
+ return 1;
+ }
+
+ use_ip6 = false;
+ 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/configs/sandbox64_defconfig b/configs/sandbox64_defconfig
index cff166b..b69e053 100644
--- a/configs/sandbox64_defconfig
+++ b/configs/sandbox64_defconfig
@@ -258,3 +258,4 @@
CONFIG_UNIT_TEST=y
CONFIG_UT_TIME=y
CONFIG_UT_DM=y
+CONFIG_IPV6=y
diff --git a/configs/sandbox_defconfig b/configs/sandbox_defconfig
index 07fe0c3..404e4a3 100644
--- a/configs/sandbox_defconfig
+++ b/configs/sandbox_defconfig
@@ -333,3 +333,4 @@
CONFIG_UNIT_TEST=y
CONFIG_UT_TIME=y
CONFIG_UT_DM=y
+CONFIG_IPV6=y
diff --git a/configs/sandbox_flattree_defconfig b/configs/sandbox_flattree_defconfig
index 01234dc..7237efe 100644
--- a/configs/sandbox_flattree_defconfig
+++ b/configs/sandbox_flattree_defconfig
@@ -216,3 +216,4 @@
CONFIG_UNIT_TEST=y
CONFIG_UT_TIME=y
CONFIG_UT_DM=y
+CONFIG_IPV6=y
diff --git a/doc/develop/release_cycle.rst b/doc/develop/release_cycle.rst
index 2bd037f..6ee67ef 100644
--- a/doc/develop/release_cycle.rst
+++ b/doc/develop/release_cycle.rst
@@ -67,7 +67,7 @@
* U-Boot v2023.01-rc2 was released on Mon 21 November 2022.
-.. * U-Boot v2023.01-rc3 was released on Mon 21 November 2022.
+* U-Boot v2023.01-rc3 was released on Mon 05 December 2022.
.. * U-Boot v2023.01-rc4 was released on Mon 05 December 2022.
diff --git a/include/env_callback.h b/include/env_callback.h
index 1eae0ef..85e7fe2 100644
--- a/include/env_callback.h
+++ b/include/env_callback.h
@@ -51,6 +51,15 @@
#define NET_CALLBACKS
#endif
+#ifdef CONFIG_IPV6
+#define NET6_CALLBACKS \
+ "ip6addr:ip6addr," \
+ "serverip6:serverip6," \
+ "gatewayip6:gatewayip6,"
+#else
+#define NET6_CALLBACKS
+#endif
+
#ifdef CONFIG_BOOTSTD
#define BOOTSTD_CALLBACK "bootmeths:bootmeths,"
#else
@@ -65,6 +74,7 @@
ENV_DOT_ESCAPE ENV_FLAGS_VAR ":flags," \
"baudrate:baudrate," \
NET_CALLBACKS \
+ NET6_CALLBACKS \
BOOTSTD_CALLBACK \
"loadaddr:loadaddr," \
SILENT_CALLBACK \
diff --git a/include/env_flags.h b/include/env_flags.h
index 313cb8c..718d727 100644
--- a/include/env_flags.h
+++ b/include/env_flags.h
@@ -67,6 +67,15 @@
#define NET_FLAGS
#endif
+#ifdef CONFIG_IPV6
+#define NET6_FLAGS \
+ "ip6addr:s," \
+ "serverip6:s," \
+ "gatewayip6:s"
+#else
+#define NET6_FLAGS
+#endif
+
#ifndef CONFIG_ENV_OVERWRITE
#define SERIAL_FLAGS "serial#:so,"
#else
@@ -76,6 +85,7 @@
#define ENV_FLAGS_LIST_STATIC \
ETHADDR_FLAGS \
NET_FLAGS \
+ NET6_FLAGS \
SERIAL_FLAGS \
CONFIG_ENV_FLAGS_LIST_STATIC
diff --git a/include/ndisc.h b/include/ndisc.h
new file mode 100644
index 0000000..f6f8eb6
--- /dev/null
+++ b/include/ndisc.h
@@ -0,0 +1,102 @@
+/* SPDX-License-Identifier: GPL-2.0+ */
+/*
+ * Copyright (C) 2013 Allied Telesis Labs NZ
+ * Chris Packham, <judge.packham@gmail.com>
+ *
+ * Copyright (C) 2022 YADRO
+ * Viacheslav Mitrofanov <v.v.mitrofanov@yadro.com>
+ */
+
+#ifndef __NDISC_H__
+#define __NDISC_H__
+
+#include <ndisc.h>
+
+/* struct nd_msg - ICMPv6 Neighbour Discovery message format */
+struct nd_msg {
+ struct icmp6hdr icmph;
+ struct in6_addr target;
+ __u8 opt[0];
+};
+
+/* struct echo_msg - ICMPv6 echo request/reply message format */
+struct echo_msg {
+ struct icmp6hdr icmph;
+ __u16 id;
+ __u16 sequence;
+};
+
+/* Neigbour Discovery option types */
+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
+};
+
+/* 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;
+
+#ifdef CONFIG_IPV6
+/**
+ * ndisc_init() - Make initial steps for ND state machine.
+ * Usually move variables into initial state.
+ */
+void ndisc_init(void);
+
+/**
+ * ndisc_receive() - Handle ND packet
+ *
+ * @et: pointer to incoming packet
+ * @ip6: pointer to IPv6 header
+ * @len: incoming packet length
+ * Return: 0 if handle successfully, -1 if unsupported/unknown ND packet type
+ */
+int ndisc_receive(struct ethernet_hdr *et, struct ip6_hdr *ip6, int len);
+
+/**
+ * ndisc_request() - Send ND request
+ */
+void ndisc_request(void);
+
+/**
+ * ndisc_init() - Check ND response timeout
+ *
+ * Return: 0 if no timeout, -1 otherwise
+ */
+int ndisc_timeout_check(void);
+#else
+static inline void ndisc_init(void)
+{
+}
+
+static inline int
+ndisc_receive(struct ethernet_hdr *et, struct ip6_hdr *ip6, int len)
+{
+ return -1;
+}
+
+static inline void ndisc_request(void)
+{
+}
+
+static inline int ndisc_timeout_check(void)
+{
+ return 0;
+}
+#endif
+
+#endif /* __NDISC_H__ */
diff --git a/include/net.h b/include/net.h
index e0c7804..1a99009 100644
--- a/include/net.h
+++ b/include/net.h
@@ -560,8 +560,8 @@
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, FASTBOOT, WOL, UDP, NCSI, WGET
+ BOOTP, RARP, ARP, TFTPGET, DHCP, PING, PING6, DNS, NFS, CDP, NETCONS,
+ SNTP, TFTPSRV, TFTPPUT, LINKLOCAL, FASTBOOT, WOL, UDP, NCSI, WGET
};
extern char net_boot_file_name[1024];/* Boot File name */
diff --git a/include/net6.h b/include/net6.h
new file mode 100644
index 0000000..9b3de02
--- /dev/null
+++ b/include/net6.h
@@ -0,0 +1,432 @@
+/* SPDX-License-Identifier: GPL-2.0+ */
+/*
+ * Copyright (C) 2013 Allied Telesis Labs NZ
+ * Chris Packham, <judge.packham@gmail.com>
+ *
+ * Copyright (C) 2022 YADRO
+ * Viacheslav Mitrofanov <v.v.mitrofanov@yadro.com>
+ */
+
+#ifndef __NET6_H__
+#define __NET6_H__
+
+#include <net.h>
+#include <linux/ctype.h>
+
+/* struct in6_addr - 128 bits long IPv6 address */
+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
+};
+
+#define IN6ADDRSZ sizeof(struct in6_addr)
+#define INETHADDRSZ sizeof(net_ethaddr)
+
+#define PROT_IP6 0x86DD /* IPv6 protocol */
+#define PROT_ICMPV6 58 /* ICMPv6 protocol*/
+
+#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
+
+#define USE_IP6_CMD_PARAM "-ipv6"
+
+/**
+ * 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;
+};
+#define IP6_HDR_SIZE (sizeof(struct ip6_hdr))
+
+/* struct udp_hdr - User Datagram Protocol header */
+struct udp_hdr {
+ u16 udp_src; /* UDP source port */
+ u16 udp_dst; /* UDP destination port */
+ u16 udp_len; /* Length of UDP packet */
+ u16 udp_xsum; /* Checksum */
+} __packed;
+
+/*
+ * 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
+
+/* hop limit for neighbour discovery packets */
+#define IPV6_NDISC_HOPLIMIT 255
+#define NDISC_TIMEOUT 5000UL
+#define NDISC_TIMEOUT_COUNT 3
+
+/* struct icmp6hdr - Internet Control Message Protocol header for IPV6 */
+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;
+
+ /* ICMPv6 data */
+ union {
+ __be32 un_data32[1];
+ __be16 un_data16[2];
+ u8 un_data8[4];
+
+ /* struct icmpv6_echo - echo request/reply message format */
+ struct icmpv6_echo {
+ __be16 identifier;
+ __be16 sequence;
+ } u_echo;
+
+ /* struct icmpv6_nd_advt - Neighbor Advertisement format */
+ 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 - Router Advertisement format */
+ 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
+};
+
+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 u32 net_prefix_length; /* Our prefixlength (0 = unknown) */
+extern struct in6_addr net_server_ip6; /* Server IPv6 addr (0 = unknown) */
+extern struct in6_addr net_ping_ip6; /* the ipv6 address to ping */
+extern bool use_ip6;
+
+#if IS_ENABLED(CONFIG_IPV6)
+/**
+ * string_to_ip6() - Convert IPv6 string addr to inner IPV6 addr format
+ *
+ * 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)
+ *
+ * @s: IPv6 string addr format
+ * @len: IPv6 string addr length
+ * @addr: converted IPv6 addr
+ * Return: 0 if conversion successful, -EINVAL if fail
+ */
+int string_to_ip6(const char *s, size_t len, struct in6_addr *addr);
+
+/**
+ * ip6_is_unspecified_addr() - Check if IPv6 addr is not set i.e. is zero
+ *
+ * @addr: IPv6 addr
+ * Return: 0 if addr is not set, -1 if is set
+ */
+int ip6_is_unspecified_addr(struct in6_addr *addr);
+
+/**
+ * ip6_is_our_addr() - Check if IPv6 addr belongs to our host addr
+ *
+ * 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.
+ *
+ * @addr: addr to check
+ * Return: 0 if addr is our, -1 otherwise
+ */
+int ip6_is_our_addr(struct in6_addr *addr);
+
+/**
+ * ip6_addr_in_subnet() - Check if two IPv6 addresses are in the same subnet
+ *
+ * @our_addr: first IPv6 addr
+ * @neigh_addr: second IPv6 addr
+ * @prefix_length: network mask length
+ * Return: 0 if two addresses in the same subnet, -1 otherwise
+ */
+int ip6_addr_in_subnet(struct in6_addr *our_addr, struct in6_addr *neigh_addr,
+ u32 prefix_length);
+
+/**
+ * ip6_make_lladd() - rMake up IPv6 Link Local address
+ *
+ * @lladdr: formed IPv6 Link Local address
+ * @enetaddr: MAC addr of a device
+ */
+void ip6_make_lladdr(struct in6_addr *lladr, unsigned char const enetaddr[6]);
+
+/**
+ * ip6_make_snma() - aMake up Solicited Node Multicast Address from IPv6 addr
+ *
+ * @mcast_addr: formed SNMA addr
+ * @ip6_addr: base IPv6 addr
+ */
+void ip6_make_snma(struct in6_addr *mcast_addr, struct in6_addr *ip6_addr);
+
+/**
+ * ip6_make_mult_ethdstaddr() - Make up IPv6 multicast addr
+ *
+ * @enetaddr: MAC addr of a device
+ * @mcast_addr: formed IPv6 multicast addr
+ */
+void ip6_make_mult_ethdstaddr(unsigned char enetaddr[6],
+ struct in6_addr *mcast_addr);
+
+/**
+ * csum_partial() - Compute an internet checksum
+ *
+ * @buff: buffer to be checksummed
+ * @len: length of buffer
+ * @sum: initial sum to be added in
+ * Return: internet checksum of the buffer
+ */
+unsigned int csum_partial(const unsigned char *buff, int len, unsigned int sum);
+
+/**
+ * csum_ipv6_magic() - Compute checksum of IPv6 "psuedo-header" per RFC2460 section 8.1
+ *
+ * @saddr: source IPv6 addr
+ * @daddr: destination IPv6 add
+ * @len: data length to be checksummed
+ * @proto: IPv6 above protocol code
+ * @csum: upper layer checksum
+ * Return: computed checksum
+ */
+unsigned short int csum_ipv6_magic(struct in6_addr *saddr,
+ struct in6_addr *daddr, u16 len,
+ unsigned short proto, unsigned int csum);
+
+/**
+ * ip6_add_hdr() - Make up IPv6 header
+ *
+ * @xip: pointer to IPv6 header to be formed
+ * @src: source IPv6 addr
+ * @dest: destination IPv6 addr
+ * @nextheader: next header type
+ * @hoplimit: hop limit
+ * @payload_len: payload length
+ * Return: IPv6 header length
+ */
+int ip6_add_hdr(uchar *xip, struct in6_addr *src, struct in6_addr *dest,
+ int nextheader, int hoplimit, int payload_len);
+
+/**
+ * net_send_udp_packet6() - Make up UDP packet and send it
+ *
+ * @ether: destination MAC addr
+ * @dest: destination IPv6 addr
+ * @dport: destination port
+ * @sport: source port
+ * @len: UDP packet length
+ * Return: 0 if send successfully, -1 otherwise
+ */
+int net_send_udp_packet6(uchar *ether, struct in6_addr *dest, int dport,
+ int sport, int len);
+
+/**
+ * net_ip6_handler() - Handle IPv6 packet
+ *
+ * @et: pointer to the beginning of the packet
+ * @ip6: pointer to the beginning of IPv6 protocol
+ * @len: incoming packet len
+ * Return: 0 if handle packet successfully, -EINVAL in case of invalid protocol
+ */
+int net_ip6_handler(struct ethernet_hdr *et, struct ip6_hdr *ip6, int len);
+
+/**
+ * net_copy_ip6() - Copy IPv6 addr
+ *
+ * @to: destination IPv6 addr
+ * @from: source IPv6 addr
+ */
+static inline void net_copy_ip6(void *to, const void *from)
+{
+ memcpy((void *)to, from, sizeof(struct in6_addr));
+}
+#else
+static inline int
+string_to_ip6(const char *s, size_t len, struct in6_addr *addr)
+{
+ return -EINVAL;
+}
+
+static inline int ip6_is_unspecified_addr(struct in6_addr *addr)
+{
+ return -1;
+}
+
+static inline int ip6_is_our_addr(struct in6_addr *addr)
+{
+ return -1;
+}
+
+static inline int
+ip6_addr_in_subnet(struct in6_addr *our_addr, struct in6_addr *neigh_addr,
+ u32 prefix_length)
+{
+ return -1;
+}
+
+static inline void
+ip6_make_lladdr(struct in6_addr *lladdr, unsigned char const enetaddr[6])
+{
+}
+
+static inline void
+ip6_make_snma(struct in6_addr *mcast_addr, struct in6_addr *ip6_addr)
+{
+}
+
+static inline void
+ip6_make_mult_ethdstaddr(unsigned char enetaddr[6],
+ struct in6_addr *mcast_addr)
+{
+}
+
+static inline unsigned int
+csum_partial(const unsigned char *buff, int len, unsigned int sum)
+{
+ return 0;
+}
+
+static inline unsigned short
+csum_ipv6_magic(struct in6_addr *saddr,
+ struct in6_addr *daddr, u16 len,
+ unsigned short proto, unsigned int csum)
+{
+ return 0;
+}
+
+static inline unsigned int
+ip6_add_hdr(uchar *xip, struct in6_addr *src, struct in6_addr *dest,
+ int nextheader, int hoplimit, int payload_len)
+{
+ return 0;
+}
+
+static inline int
+net_send_udp_packet6(uchar *ether, struct in6_addr *dest,
+ int dport, int sport, int len)
+{
+ return -1;
+}
+
+static inline int
+net_ip6_handler(struct ethernet_hdr *et, struct ip6_hdr *ip6,
+ int len)
+{
+ return -EINVAL;
+}
+
+static inline void net_copy_ip6(void *to, const void *from)
+{
+}
+#endif
+
+#if IS_ENABLED(CONFIG_CMD_PING6)
+/* Send ping requset */
+void ping6_start(void);
+
+/**
+ * ping6_receive() - Handle reception of ICMPv6 echo request/reply
+ *
+ * @et: pointer to incoming patcket
+ * @ip6: pointer to IPv6 protocol
+ * @len: packet length
+ * Return: 0 if success, -EINVAL in case of failure during reception
+ */
+int ping6_receive(struct ethernet_hdr *et, struct ip6_hdr *ip6, int len);
+#else
+static inline void ping6_start(void)
+{
+}
+
+static inline
+int ping6_receive(struct ethernet_hdr *et, struct ip6_hdr *ip6, int len)
+{
+ return -EINVAL;
+}
+#endif /* CONFIG_CMD_PING6 */
+
+#endif /* __NET6_H__ */
diff --git a/lib/net_utils.c b/lib/net_utils.c
index 72a3b09..4283c13 100644
--- a/lib/net_utils.c
+++ b/lib/net_utils.c
@@ -11,6 +11,7 @@
#include <common.h>
#include <net.h>
+#include <net6.h>
struct in_addr string_to_ip(const char *s)
{
@@ -43,6 +44,114 @@
return addr;
}
+#if IS_ENABLED(CONFIG_IPV6)
+int string_to_ip6(const char *str, size_t len, struct in6_addr *addr)
+{
+ int colon_count = 0;
+ int found_double_colon = 0;
+ int xstart = 0; /* first zero (double colon) */
+ int section_num = 7; /* num words the double colon represents */
+ int i;
+ const char *s = str;
+ const char *const e = s + len;
+ struct in_addr zero_ip = {.s_addr = 0};
+
+ if (!str)
+ return -1;
+
+ /* First pass, verify the syntax and locate the double colon */
+ while (s < e) {
+ while (s < e && isxdigit((int)*s))
+ s++;
+ if (*s == '\0')
+ break;
+ if (*s != ':') {
+ if (*s == '.' && section_num >= 2) {
+ struct in_addr v4;
+
+ while (s != str && *(s - 1) != ':')
+ --s;
+ v4 = string_to_ip(s);
+ if (memcmp(&zero_ip, &v4,
+ sizeof(struct in_addr)) != 0) {
+ section_num -= 2;
+ break;
+ }
+ }
+ /* This could be a valid address */
+ break;
+ }
+ if (s == str) {
+ /* 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)
+ section_num--;
+ else
+ xstart++;
+ }
+
+ if (*s == ':') {
+ if (found_double_colon)
+ /* Two double colons are not allowed */
+ goto out_err;
+ found_double_colon = 1;
+ section_num -= xstart;
+ s++;
+ }
+
+ if (++colon_count == 7)
+ /* Found all colons */
+ break;
+ ++s;
+ }
+
+ if (colon_count == 0)
+ goto out_err;
+ if (*--s == ':')
+ section_num++;
+
+ /* Second pass, read the address */
+ s = str;
+ for (i = 0; i < 8; i++) {
+ int val = 0;
+ char *end;
+
+ if (found_double_colon &&
+ i >= xstart && i < xstart + section_num) {
+ addr->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 */
+ addr->s6_addr32[3] = v4.s_addr;
+ break;
+ }
+ }
+
+ val = simple_strtoul(s, &end, 16);
+ if (end != e && *end != '\0' && *end != ':')
+ goto out_err;
+ addr->s6_addr16[i] = htons(val);
+ s = end;
+ }
+ return 0;
+
+out_err:
+ return -1;
+}
+#endif
+
void string_to_enetaddr(const char *addr, uint8_t *enetaddr)
{
char *end;
diff --git a/lib/vsprintf.c b/lib/vsprintf.c
index fe06aa2..530d808 100644
--- a/lib/vsprintf.c
+++ b/lib/vsprintf.c
@@ -450,10 +450,6 @@
* 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
- *
- * Note: IPv6 support is currently if(0)'ed out. If you ever need
- * %pI6, please add an IPV6 Kconfig knob, make your code select or
- * depend on that, and change the 0 below to CONFIG_IS_ENABLED(IPV6).
*/
static char *pointer(const char *fmt, char *buf, char *end, void *ptr,
int field_width, int precision, int flags)
@@ -498,8 +494,7 @@
flags |= SPECIAL;
/* Fallthrough */
case 'I':
- /* %pI6 currently unused */
- if (0 && fmt[1] == '6')
+ if (IS_ENABLED(CONFIG_IPV6) && fmt[1] == '6')
return ip6_addr_string(buf, end, ptr, field_width,
precision, flags);
if (fmt[1] == '4')
diff --git a/net/Kconfig b/net/Kconfig
index cb600fe..a1ec3f8 100644
--- a/net/Kconfig
+++ b/net/Kconfig
@@ -190,6 +190,16 @@
This option should be turn on if you want to achieve the fastest
file transfer possible.
+config IPV6
+ bool "IPv6 support"
+ help
+ Enable IPv6 support. It includes Neighbour Discovery protocol, ICMPv6
+ and auxiliary stuff to make it work. Since it is enabled u-boot
+ network subsystem can get and handle incoming packets and send packets
+ through IPv6 network. It allows to use environment variables such as
+ ip6addr, serverip6. If a u-boot command is capable to parse an IPv6
+ address and find it, it will force using IPv6 in the network stack.
+
endif # if NET
config SYS_RX_ETH_BUFFER
diff --git a/net/Makefile b/net/Makefile
index 4f757a2..13eef04 100644
--- a/net/Makefile
+++ b/net/Makefile
@@ -20,9 +20,12 @@
obj-$(CONFIG_DM_MDIO_MUX) += mdio-mux-uclass.o
obj-$(CONFIG_NET) += eth_common.o
obj-$(CONFIG_CMD_LINK_LOCAL) += link_local.o
+obj-$(CONFIG_IPV6) += ndisc.o
obj-$(CONFIG_NET) += net.o
+obj-$(CONFIG_IPV6) += net6.o
obj-$(CONFIG_CMD_NFS) += nfs.o
obj-$(CONFIG_CMD_PING) += ping.o
+obj-$(CONFIG_CMD_PING6) += ping6.o
obj-$(CONFIG_CMD_PCAP) += pcap.o
obj-$(CONFIG_CMD_RARP) += rarp.o
obj-$(CONFIG_CMD_SNTP) += sntp.o
diff --git a/net/ndisc.c b/net/ndisc.c
new file mode 100644
index 0000000..3c0eeea
--- /dev/null
+++ b/net/ndisc.c
@@ -0,0 +1,289 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright (C) 2013 Allied Telesis Labs NZ
+ * Chris Packham, <judge.packham@gmail.com>
+ *
+ * Copyright (C) 2022 YADRO
+ * Viacheslav Mitrofanov <v.v.mitrofanov@yadro.com>
+ */
+
+/* Neighbour Discovery for IPv6 */
+
+#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)
+
+/**
+ * ndisc_insert_option() - Insert an option into a neighbor discovery packet
+ *
+ * @ndisc: pointer to ND packet
+ * @type: option type to insert
+ * @data: option data to insert
+ * @len: data length
+ * Return: 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;
+}
+
+/**
+ * ndisc_extract_enetaddr() - Extract the Ethernet address from a ND 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.
+ *
+ * @ndisc: pointer to ND packet
+ * @enetaddr: extracted MAC addr
+ */
+static void ndisc_extract_enetaddr(struct nd_msg *ndisc, uchar enetaddr[6])
+{
+ memcpy(enetaddr, &ndisc->opt[2], 6);
+}
+
+/**
+ * ndisc_has_option() - Check if the ND packet has the specified option set
+ *
+ * @ip6: pointer to IPv6 header
+ * @type: option type to check
+ * Return: 1 if ND has that option, 0 therwise
+ */
+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;
+ unsigned short csum;
+ unsigned int pcsum;
+
+ 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, PROT_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 */
+ pcsum = csum_partial((__u8 *)msg, len, 0);
+ csum = csum_ipv6_magic(&net_link_local_ip6, &dst_adr,
+ len, PROT_ICMPV6, pcsum);
+ msg->icmph.icmp6_cksum = csum;
+ 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;
+ unsigned short csum;
+
+ 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,
+ PROT_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));
+ msg->icmph.icmp6_dataun.u_nd_advt.solicited = 1;
+ msg->icmph.icmp6_dataun.u_nd_advt.override = 1;
+ /* 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 */
+ csum = csum_ipv6_magic(&net_link_local_ip6,
+ neigh_addr, len, PROT_ICMPV6,
+ csum_partial((__u8 *)msg, len, 0));
+ msg->icmph.icmp6_cksum = csum;
+ 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;
+}
+
+int 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)
+ memcpy(net_nd_packet_mac, neigh_eth_addr, 7);
+
+ /* 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);
+ return -1;
+ }
+
+ return 0;
+}
diff --git a/net/net.c b/net/net.c
index aca20e4..1c39acc 100644
--- a/net/net.c
+++ b/net/net.c
@@ -91,6 +91,8 @@
#include <image.h>
#include <log.h>
#include <net.h>
+#include <net6.h>
+#include <ndisc.h>
#include <net/fastboot.h>
#include <net/tftp.h>
#include <net/ncsi.h>
@@ -343,8 +345,17 @@
static int net_init_loop(void)
{
- if (eth_get_dev())
+ if (eth_get_dev()) {
memcpy(net_ethaddr, eth_get_ethaddr(), 6);
+
+ if (IS_ENABLED(CONFIG_IPV6)) {
+ ip6_make_lladdr(&net_link_local_ip6, net_ethaddr);
+ if (!memcmp(&net_ip6, &net_null_addr_ip6,
+ sizeof(struct in6_addr)))
+ memcpy(&net_ip6, &net_link_local_ip6,
+ sizeof(struct in6_addr));
+ }
+ }
else
/*
* Not ideal, but there's no way to get the actual error, and I
@@ -385,6 +396,7 @@
(i + 1) * PKTSIZE_ALIGN;
}
arp_init();
+ ndisc_init();
net_clear_handlers();
/* Only need to setup buffer pointers once. */
@@ -513,6 +525,11 @@
ping_start();
break;
#endif
+#if defined(CONFIG_CMD_PING6)
+ case PING6:
+ ping6_start();
+ break;
+#endif
#if defined(CONFIG_CMD_NFS) && !defined(CONFIG_SPL_BUILD)
case NFS:
nfs_start();
@@ -589,6 +606,11 @@
if (arp_timeout_check() > 0)
time_start = get_timer(0);
+ if (IS_ENABLED(CONFIG_IPV6)) {
+ if (use_ip6 && (ndisc_timeout_check() > 0))
+ time_start = get_timer(0);
+ }
+
/*
* Check the ethernet for a new packet. The ethernet
* receive routine will process it.
@@ -1244,6 +1266,10 @@
rarp_receive(ip, len);
break;
#endif
+#if IS_ENABLED(CONFIG_IPV6)
+ case PROT_IP6:
+ net_ip6_handler(et, (struct ip6_hdr *)ip, len);
+#endif
case PROT_IP:
debug_cond(DEBUG_NET_PKT, "Got IP\n");
/* Before we start poking the header, make sure it is there */
@@ -1413,6 +1439,14 @@
}
goto common;
#endif
+#if defined(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_DNS)
case DNS:
if (net_dns_server.s_addr == 0) {
@@ -1434,7 +1468,14 @@
/* Fall through */
case TFTPGET:
case TFTPPUT:
- if (net_server_ip.s_addr == 0 && !is_serverip_in_cmd()) {
+ if (IS_ENABLED(CONFIG_IPV6) && use_ip6) {
+ if (!memcmp(&net_server_ip6, &net_null_addr_ip6,
+ sizeof(struct in6_addr)) &&
+ !strchr(net_boot_file_name, '[')) {
+ puts("*** ERROR: `serverip6' not set\n");
+ return 1;
+ }
+ } else if (net_server_ip.s_addr == 0 && !is_serverip_in_cmd()) {
puts("*** ERROR: `serverip' not set\n");
return 1;
}
@@ -1447,7 +1488,13 @@
case NETCONS:
case FASTBOOT:
case TFTPSRV:
- if (net_ip.s_addr == 0) {
+ if (IS_ENABLED(CONFIG_IPV6) && use_ip6) {
+ if (!memcmp(&net_link_local_ip6, &net_null_addr_ip6,
+ sizeof(struct in6_addr))) {
+ puts("*** ERROR: `ip6addr` not set\n");
+ return 1;
+ }
+ } else if (net_ip.s_addr == 0) {
puts("*** ERROR: `ipaddr' not set\n");
return 1;
}
diff --git a/net/net6.c b/net/net6.c
new file mode 100644
index 0000000..fdea078
--- /dev/null
+++ b/net/net6.c
@@ -0,0 +1,445 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright (C) 2013 Allied Telesis Labs NZ
+ * Chris Packham, <judge.packham@gmail.com>
+ *
+ * Copyright (C) 2022 YADRO
+ * Viacheslav Mitrofanov <v.v.mitrofanov@yadro.com>
+ */
+
+/* Simple IPv6 network layer implementation */
+
+#include <common.h>
+#include <env_internal.h>
+#include <malloc.h>
+#include <net.h>
+#include <net6.h>
+#include <ndisc.h>
+
+/* NULL IPv6 address */
+struct in6_addr const net_null_addr_ip6 = ZERO_IPV6_ADDR;
+/* Our gateway's IPv6 address */
+struct in6_addr net_gateway6 = ZERO_IPV6_ADDR;
+/* Our IPv6 addr (0 = unknown) */
+struct in6_addr net_ip6 = ZERO_IPV6_ADDR;
+/* Our link local IPv6 addr (0 = unknown) */
+struct in6_addr net_link_local_ip6 = ZERO_IPV6_ADDR;
+/* set server IPv6 addr (0 = unknown) */
+struct in6_addr net_server_ip6 = ZERO_IPV6_ADDR;
+/* The prefix length of our network */
+u32 net_prefix_length;
+
+bool use_ip6;
+
+static int on_ip6addr(const char *name, const char *value, enum env_op op,
+ int flags)
+{
+ char *mask;
+ size_t len;
+
+ 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;
+ }
+
+ mask = strchr(value, '/');
+ len = strlen(value);
+
+ if (mask)
+ net_prefix_length = simple_strtoul(value + len, NULL, 10);
+
+ return string_to_ip6(value, len, &net_ip6);
+}
+
+U_BOOT_ENV_CALLBACK(ip6addr, on_ip6addr);
+
+static int on_gatewayip6(const char *name, const char *value, enum env_op op,
+ int flags)
+{
+ if (flags & H_PROGRAMMATIC)
+ return 0;
+
+ return string_to_ip6(value, strlen(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, strlen(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]);
+}
+
+int ip6_is_our_addr(struct in6_addr *addr)
+{
+ return !memcmp(addr, &net_link_local_ip6, sizeof(struct in6_addr)) ||
+ !memcmp(addr, &net_ip6, sizeof(struct in6_addr));
+}
+
+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])
+{
+ unsigned char 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);
+}
+
+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];
+}
+
+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);
+
+ /* Opaque moment. If reverse it to zero it will not be checked on
+ * receiver's side. It leads to bad negibour advertisement.
+ */
+ if (sum == 0xffff)
+ return sum;
+
+ return ~sum;
+}
+
+static inline unsigned short from32to16(unsigned int x)
+{
+ /* add up 16-bit and 16-bit for 16+c bit */
+ x = (x & 0xffff) + (x >> 16);
+ /* add up carry.. */
+ x = (x & 0xffff) + (x >> 16);
+ return x;
+}
+
+static u32 csum_do_csum(const u8 *buff, int len)
+{
+ int odd;
+ unsigned int result = 0;
+
+ if (len <= 0)
+ goto out;
+ odd = 1 & (unsigned long)buff;
+ if (odd) {
+#ifdef __LITTLE_ENDIAN
+ result += (*buff << 8);
+#else
+ result = *buff;
+#endif
+ len--;
+ buff++;
+ }
+ if (len >= 2) {
+ if (2 & (unsigned long)buff) {
+ result += *(unsigned short *)buff;
+ len -= 2;
+ buff += 2;
+ }
+ if (len >= 4) {
+ const unsigned char *end = buff + ((u32)len & ~3);
+ unsigned int carry = 0;
+
+ do {
+ unsigned int w = *(unsigned int *)buff;
+
+ buff += 4;
+ result += carry;
+ result += w;
+ carry = (w > result);
+ } while (buff < end);
+ result += carry;
+ result = (result & 0xffff) + (result >> 16);
+ }
+ if (len & 2) {
+ result += *(unsigned short *)buff;
+ buff += 2;
+ }
+ }
+ if (len & 1)
+#ifdef __LITTLE_ENDIAN
+ result += *buff;
+#else
+ result += (*buff << 8);
+#endif
+ result = from32to16(result);
+ if (odd)
+ result = ((result >> 8) & 0xff) | ((result & 0xff) << 8);
+out:
+ return result;
+}
+
+unsigned int csum_partial(const unsigned char *buff, int len, unsigned int sum)
+{
+ unsigned int result = csum_do_csum(buff, len);
+
+ /* add in old sum, and carry.. */
+ result += sum;
+ /* 16+c bits -> 16 bits */
+ result = (result & 0xffff) + (result >> 16);
+ return result;
+}
+
+unsigned short int
+csum_ipv6_magic(struct in6_addr *saddr, struct in6_addr *daddr, u16 len,
+ unsigned short proto, unsigned int csum)
+{
+ int carry;
+ u32 ulen;
+ u32 uproto;
+ u32 sum = csum;
+
+ sum += saddr->s6_addr32[0];
+ carry = (sum < saddr->s6_addr32[0]);
+ sum += carry;
+
+ sum += saddr->s6_addr32[1];
+ carry = (sum < saddr->s6_addr32[1]);
+ sum += carry;
+
+ sum += saddr->s6_addr32[2];
+ carry = (sum < saddr->s6_addr32[2]);
+ sum += carry;
+
+ sum += saddr->s6_addr32[3];
+ carry = (sum < saddr->s6_addr32[3]);
+ sum += carry;
+
+ sum += daddr->s6_addr32[0];
+ carry = (sum < daddr->s6_addr32[0]);
+ sum += carry;
+
+ sum += daddr->s6_addr32[1];
+ carry = (sum < daddr->s6_addr32[1]);
+ sum += carry;
+
+ sum += daddr->s6_addr32[2];
+ carry = (sum < daddr->s6_addr32[2]);
+ sum += carry;
+
+ sum += daddr->s6_addr32[3];
+ carry = (sum < daddr->s6_addr32[3]);
+ sum += carry;
+
+ ulen = htonl((u32)len);
+ sum += ulen;
+ carry = (sum < ulen);
+ sum += carry;
+
+ uproto = htonl(proto);
+ sum += uproto;
+ carry = (sum < uproto);
+ sum += carry;
+
+ return csum_fold(sum);
+}
+
+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);
+}
+
+int net_send_udp_packet6(uchar *ether, struct in6_addr *dest, int dport,
+ int sport, int len)
+{
+ uchar *pkt;
+ struct udp_hdr *udp;
+ u16 csum_p;
+
+ 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;
+ csum_p = csum_partial((u8 *)udp, len + UDP_HDR_SIZE, 0);
+ udp->udp_xsum = csum_ipv6_magic(&net_ip6, dest, len + UDP_HDR_SIZE,
+ IPPROTO_UDP, csum_p);
+
+ /* if MAC address was not discovered yet, save the packet and do
+ * neighbour discovery
+ */
+ if (!memcmp(ether, net_null_ethaddr, 6)) {
+ 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 */
+}
+
+int 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 csum_p;
+ u16 hlen;
+
+ if (len < IP6_HDR_SIZE)
+ return -EINVAL;
+
+ if (ip6->version != 6)
+ return -EINVAL;
+
+ switch (ip6->nexthdr) {
+ case PROT_ICMPV6:
+ icmp = (struct icmp6hdr *)(((uchar *)ip6) + IP6_HDR_SIZE);
+ csum = icmp->icmp6_cksum;
+ hlen = ntohs(ip6->payload_len);
+ icmp->icmp6_cksum = 0;
+ /* checksum */
+ csum_p = csum_partial((u8 *)icmp, hlen, 0);
+ icmp->icmp6_cksum = csum_ipv6_magic(&ip6->saddr, &ip6->daddr,
+ hlen, PROT_ICMPV6, csum_p);
+
+ if (icmp->icmp6_cksum != csum)
+ return -EINVAL;
+
+ switch (icmp->icmp6_type) {
+ case IPV6_ICMP_ECHO_REQUEST:
+ case IPV6_ICMP_ECHO_REPLY:
+ ping6_receive(et, ip6, len);
+ break;
+ case IPV6_NDISC_NEIGHBOUR_SOLICITATION:
+ case IPV6_NDISC_NEIGHBOUR_ADVERTISEMENT:
+ ndisc_receive(et, ip6, len);
+ break;
+ default:
+ break;
+ }
+ 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 */
+ csum_p = csum_partial((u8 *)udp, hlen, 0);
+ udp->udp_xsum = csum_ipv6_magic(&ip6->saddr, &ip6->daddr,
+ hlen, IPPROTO_UDP, csum_p);
+
+ if (csum != udp->udp_xsum)
+ return -EINVAL;
+
+ /* 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 -EINVAL;
+ }
+
+ return 0;
+}
diff --git a/net/ping6.c b/net/ping6.c
new file mode 100644
index 0000000..4882a17
--- /dev/null
+++ b/net/ping6.c
@@ -0,0 +1,118 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright (C) 2013 Allied Telesis Labs NZ
+ * Chris Packham, <judge.packham@gmail.com>
+ *
+ * Copyright (C) 2022 YADRO
+ * Viacheslav Mitrofanov <v.v.mitrofanov@yadro.com>
+ */
+
+/* Simple ping6 implementation */
+
+#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;
+ u16 csum_p;
+ 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, PROT_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 */
+ csum_p = csum_partial((u8 *)msg, len, 0);
+ msg->icmph.icmp6_cksum = csum_ipv6_magic(&net_ip6, neigh_addr, len,
+ PROT_ICMPV6, csum_p);
+
+ 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();
+}
+
+int 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)))
+ return -EINVAL;
+ net_set_state(NETLOOP_SUCCESS);
+ break;
+ case IPV6_ICMP_ECHO_REQUEST:
+ /* ignore for now.... */
+ debug("Got ICMPv6 ECHO REQUEST from %pI6c\n", &ip6->saddr);
+ return -EINVAL;
+ default:
+ debug("Unexpected ICMPv6 type 0x%x\n", icmp->icmp6_type);
+ return -EINVAL;
+ }
+
+ return 0;
+}
diff --git a/net/tftp.c b/net/tftp.c
index 39421f8..c780c33 100644
--- a/net/tftp.c
+++ b/net/tftp.c
@@ -15,6 +15,7 @@
#include <log.h>
#include <mapmem.h>
#include <net.h>
+#include <net6.h>
#include <asm/global_data.h>
#include <net/tftp.h>
#include "bootp.h"
@@ -41,6 +42,7 @@
static ulong timeout_ms = TIMEOUT;
static int timeout_count_max = (CONFIG_NET_RETRY_COUNT * 2);
static ulong time_start; /* Record time we started tftp */
+static struct in6_addr tftp_remote_ip6;
/*
* These globals govern the timeout behavior when attempting a connection to a
@@ -116,6 +118,7 @@
/* default TFTP block size */
#define TFTP_BLOCK_SIZE 512
+#define TFTP_MTU_BLOCKSIZE6 (CONFIG_TFTP_BLOCKSIZE - 20)
/* sequence number is 16 bit */
#define TFTP_SEQUENCE_SIZE ((ulong)(1<<16))
@@ -320,7 +323,11 @@
* We will always be sending some sort of packet, so
* cobble together the packet headers now.
*/
- pkt = net_tx_packet + net_eth_hdr_size() + IP_UDP_HDR_SIZE;
+ if (IS_ENABLED(CONFIG_IPV6) && use_ip6)
+ pkt = net_tx_packet + net_eth_hdr_size() +
+ IP6_HDR_SIZE + UDP_HDR_SIZE;
+ else
+ pkt = net_tx_packet + net_eth_hdr_size() + IP_UDP_HDR_SIZE;
switch (tftp_state) {
case STATE_SEND_RRQ:
@@ -422,8 +429,14 @@
break;
}
- net_send_udp_packet(net_server_ethaddr, tftp_remote_ip,
- tftp_remote_port, tftp_our_port, len);
+ if (IS_ENABLED(CONFIG_IPV6) && use_ip6)
+ net_send_udp_packet6(net_server_ethaddr,
+ &tftp_remote_ip6,
+ tftp_remote_port,
+ tftp_our_port, len);
+ else
+ net_send_udp_packet(net_server_ethaddr, tftp_remote_ip,
+ tftp_remote_port, tftp_our_port, len);
if (err_pkt)
net_set_state(NETLOOP_FAIL);
@@ -797,6 +810,9 @@
debug("TFTP blocksize = %i, TFTP windowsize = %d timeout = %ld ms\n",
tftp_block_size_option, tftp_window_size_option, timeout_ms);
+ if (IS_ENABLED(CONFIG_IPV6))
+ tftp_remote_ip6 = net_server_ip6;
+
tftp_remote_ip = net_server_ip;
if (!net_parse_bootfile(&tftp_remote_ip, tftp_filename, MAX_LEN)) {
sprintf(default_filename, "%02X%02X%02X%02X.img",
@@ -812,17 +828,49 @@
tftp_filename);
}
+ if (IS_ENABLED(CONFIG_IPV6)) {
+ if (use_ip6) {
+ char *s, *e;
+ size_t len;
+
+ s = strchr(net_boot_file_name, '[');
+ e = strchr(net_boot_file_name, ']');
+ len = e - s;
+ if (s && e) {
+ string_to_ip6(s + 1, len, &tftp_remote_ip6);
+ strlcpy(tftp_filename, e + 2, MAX_LEN);
+ } else {
+ strlcpy(tftp_filename, net_boot_file_name, MAX_LEN);
+ tftp_filename[MAX_LEN - 1] = 0;
+ }
+ }
+ }
+
printf("Using %s device\n", eth_get_name());
- printf("TFTP %s server %pI4; our IP address is %pI4",
+
+ if (IS_ENABLED(CONFIG_IPV6) && use_ip6) {
+ printf("TFTP from server %pI6c; our IP address is %pI6c",
+ &tftp_remote_ip6, &net_ip6);
+
+ if (tftp_block_size_option > TFTP_MTU_BLOCKSIZE6)
+ tftp_block_size_option = TFTP_MTU_BLOCKSIZE6;
+ } else {
+ printf("TFTP %s server %pI4; our IP address is %pI4",
#ifdef CONFIG_CMD_TFTPPUT
protocol == TFTPPUT ? "to" : "from",
#else
"from",
#endif
&tftp_remote_ip, &net_ip);
+ }
/* Check if we need to send across this subnet */
- if (net_gateway.s_addr && net_netmask.s_addr) {
+ if (IS_ENABLED(CONFIG_IPV6) && use_ip6) {
+ if (!ip6_addr_in_subnet(&net_ip6, &tftp_remote_ip6,
+ net_prefix_length))
+ printf("; sending through gateway %pI6c",
+ &net_gateway6);
+ } else if (net_gateway.s_addr && net_netmask.s_addr) {
struct in_addr our_net;
struct in_addr remote_net;
diff --git a/test/dm/eth.c b/test/dm/eth.c
index 5437f9e..ebf01d8 100644
--- a/test/dm/eth.c
+++ b/test/dm/eth.c
@@ -13,6 +13,7 @@
#include <log.h>
#include <malloc.h>
#include <net.h>
+#include <net6.h>
#include <asm/eth.h>
#include <dm/test.h>
#include <dm/device-internal.h>
@@ -22,6 +23,152 @@
#define DM_TEST_ETH_NUM 4
+#if IS_ENABLED(CONFIG_IPV6)
+static int dm_test_string_to_ip6(struct unit_test_state *uts)
+{
+ char *str;
+ struct test_ip6_pair {
+ char *string_addr;
+ struct in6_addr ip6_addr;
+ };
+
+ struct in6_addr ip6 = {0};
+
+ /* Correct statements */
+ struct test_ip6_pair test_suite[] = {
+ {"2001:db8::0:1234:1", {.s6_addr32[0] = 0xb80d0120,
+ .s6_addr32[1] = 0x00000000,
+ .s6_addr32[2] = 0x00000000,
+ .s6_addr32[3] = 0x01003412}},
+ {"2001:0db8:0000:0000:0000:0000:1234:0001",
+ {.s6_addr32[0] = 0xb80d0120,
+ .s6_addr32[1] = 0x00000000,
+ .s6_addr32[2] = 0x00000000,
+ .s6_addr32[3] = 0x01003412}},
+ {"::1", {.s6_addr32[0] = 0x00000000,
+ .s6_addr32[1] = 0x00000000,
+ .s6_addr32[2] = 0x00000000,
+ .s6_addr32[3] = 0x01000000}},
+ {"::ffff:192.168.1.1", {.s6_addr32[0] = 0x00000000,
+ .s6_addr32[1] = 0x00000000,
+ .s6_addr32[2] = 0xffff0000,
+ .s6_addr32[3] = 0x0101a8c0}},
+ };
+
+ for (int i = 0; i < ARRAY_SIZE(test_suite); ++i) {
+ ut_assertok(string_to_ip6(test_suite[i].string_addr,
+ strlen(test_suite[i].string_addr), &ip6));
+ ut_asserteq_mem(&ip6, &test_suite[i].ip6_addr,
+ sizeof(struct in6_addr));
+ }
+
+ /* Incorrect statements */
+ str = "hello:world";
+ ut_assertok(!string_to_ip6(str, strlen(str), &ip6));
+ str = "2001:db8::0::0";
+ ut_assertok(!string_to_ip6(str, strlen(str), &ip6));
+ str = "2001:db8:192.168.1.1::1";
+ ut_assertok(!string_to_ip6(str, strlen(str), &ip6));
+ str = "192.168.1.1";
+ ut_assertok(!string_to_ip6(str, strlen(str), &ip6));
+
+ return 0;
+}
+DM_TEST(dm_test_string_to_ip6, 0);
+
+static int dm_test_csum_ipv6_magic(struct unit_test_state *uts)
+{
+ unsigned short csum = 0xbeef;
+ /* Predefined correct parameters */
+ unsigned short correct_csum = 0xd8ac;
+ struct in6_addr saddr = {.s6_addr32[0] = 0x000080fe,
+ .s6_addr32[1] = 0x00000000,
+ .s6_addr32[2] = 0xffe9f242,
+ .s6_addr32[3] = 0xe8f66dfe};
+ struct in6_addr daddr = {.s6_addr32[0] = 0x000080fe,
+ .s6_addr32[1] = 0x00000000,
+ .s6_addr32[2] = 0xffd5b372,
+ .s6_addr32[3] = 0x3ef692fe};
+ u16 len = 1460;
+ unsigned short proto = 17;
+ unsigned int head_csum = 0x91f0;
+
+ csum = csum_ipv6_magic(&saddr, &daddr, len, proto, head_csum);
+ ut_asserteq(csum, correct_csum);
+
+ /* Broke a parameter */
+ proto--;
+ csum = csum_ipv6_magic(&saddr, &daddr, len, proto, head_csum);
+ ut_assert(csum != correct_csum);
+
+ return 0;
+}
+DM_TEST(dm_test_csum_ipv6_magic, 0);
+
+static int dm_test_ip6_addr_in_subnet(struct unit_test_state *uts)
+{
+ struct in6_addr our = {.s6_addr32[0] = 0x000080fe,
+ .s6_addr32[1] = 0x00000000,
+ .s6_addr32[2] = 0xffe9f242,
+ .s6_addr32[3] = 0xe8f66dfe};
+ struct in6_addr neigh1 = {.s6_addr32[0] = 0x000080fe,
+ .s6_addr32[1] = 0x00000000,
+ .s6_addr32[2] = 0xffd5b372,
+ .s6_addr32[3] = 0x3ef692fe};
+ struct in6_addr neigh2 = {.s6_addr32[0] = 0x60480120,
+ .s6_addr32[1] = 0x00006048,
+ .s6_addr32[2] = 0x00000000,
+ .s6_addr32[3] = 0x00008888};
+
+ /* in */
+ ut_assert(ip6_addr_in_subnet(&our, &neigh1, 64));
+ /* outside */
+ ut_assert(!ip6_addr_in_subnet(&our, &neigh2, 64));
+ ut_assert(!ip6_addr_in_subnet(&our, &neigh1, 128));
+
+ return 0;
+}
+DM_TEST(dm_test_ip6_addr_in_subnet, 0);
+
+static int dm_test_ip6_make_snma(struct unit_test_state *uts)
+{
+ struct in6_addr mult = {0};
+ struct in6_addr correct_addr = {
+ .s6_addr32[0] = 0x000002ff,
+ .s6_addr32[1] = 0x00000000,
+ .s6_addr32[2] = 0x01000000,
+ .s6_addr32[3] = 0xe8f66dff};
+ struct in6_addr addr = { .s6_addr32[0] = 0x000080fe,
+ .s6_addr32[1] = 0x00000000,
+ .s6_addr32[2] = 0xffe9f242,
+ .s6_addr32[3] = 0xe8f66dfe};
+
+ ip6_make_snma(&mult, &addr);
+ ut_asserteq_mem(&mult, &correct_addr, sizeof(struct in6_addr));
+
+ return 0;
+}
+DM_TEST(dm_test_ip6_make_snma, 0);
+
+static int dm_test_ip6_make_lladdr(struct unit_test_state *uts)
+{
+ struct in6_addr generated_lladdr = {0};
+ struct in6_addr correct_lladdr = {
+ .s6_addr32[0] = 0x000080fe,
+ .s6_addr32[1] = 0x00000000,
+ .s6_addr32[2] = 0xffabf33a,
+ .s6_addr32[3] = 0xfbb352fe};
+ const unsigned char mac[6] = {0x38, 0xf3, 0xab, 0x52, 0xb3, 0xfb};
+
+ ip6_make_lladdr(&generated_lladdr, mac);
+ ut_asserteq_mem(&generated_lladdr, &correct_lladdr,
+ sizeof(struct in6_addr));
+
+ return 0;
+}
+DM_TEST(dm_test_ip6_make_lladdr, UT_TESTF_SCAN_FDT);
+#endif
+
static int dm_test_eth(struct unit_test_state *uts)
{
net_ping_ip = string_to_ip("1.1.2.2");