[U-Boot] [PATCH 1/2] [RFC]new command: wol - Wake on LAN

This patch adds a new command 'wol': It waits for an incoming Wake-on-LAN packet or times out if no WoL packed is received. If the WoL packet contains a password, it is saved in the environment variable 'wolpassword' using the etherwake format (dot separated decimals).
Intended use case: a networked device should boot an alternate image. It's attached to a network on a client site, modifying the DHCP server configuration or setup of a tftp server is not allowed. After power on the device waits a few seconds for a WoL packet. If a packet is received, the device boots the alternate image. Otherwise it boots the default image.
This method is a simple way to interact with a system via network even if only the MAC address is known. Tools to send WoL packets are available on all common platforms.
Signed-off-by: Lothar Felten lothar.felten@gmail.com --- net/wol.c | 54 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ net/wol.h | 40 ++++++++++++++++++++++++++++++++++++++++ 2 files changed, 94 insertions(+) create mode 100644 net/wol.c create mode 100644 net/wol.h
diff --git a/net/wol.c b/net/wol.c new file mode 100644 index 0000000000..0cedbaed85 --- /dev/null +++ b/net/wol.c @@ -0,0 +1,54 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright 2018 Lothar Felten, lothar.felten@gmail.com + */ + +#include <common.h> +#include <command.h> +#include <net.h> +#include "wol.h" + +/* + * Handle a received wake-on-lan packet. + */ +void wol_receive(struct ip_udp_hdr *ip, unsigned int len) +{ + char buf[4]; + int i; + struct wol_hdr *wol; + + wol = (struct wol_hdr *)ip; + + if (len < 102) + return; + + for (i = 0; i < 6; i++) + if (wol->wol_sync[i] != 0xff) + return; + + for (i = 0; i < 16; i++) + if (memcmp(&wol->wol_dest[i * 6], net_ethaddr, 6) != 0) + return; + + /* save the optional password using the etherwake format */ + if (len >= 106) { + sprintf(buf, "%i.%i.%i.%i", + wol->wol_passwd[0], wol->wol_passwd[1], + wol->wol_passwd[2], wol->wol_passwd[3]); + env_set("wolpassword", buf); + } + net_set_state(NETLOOP_SUCCESS); +} + +static void wol_timeout_handler(void) +{ + eth_halt(); + net_set_state(NETLOOP_FAIL); +} + +void wol_start(void) +{ + ulong timeout = env_get_ulong("woltimeout", 10, 5); + + net_set_timeout_handler(timeout * 1000, wol_timeout_handler); +} diff --git a/net/wol.h b/net/wol.h new file mode 100644 index 0000000000..e34767c733 --- /dev/null +++ b/net/wol.h @@ -0,0 +1,40 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* + * Copyright 2018 Lothar Felten, lothar.felten@gmail.com + */ + +#if defined(CONFIG_CMD_WOL) + +#ifndef __WOL_H__ +#define __WOL_H__ + +#include <net.h> + +/**********************************************************************/ + +/* + * Wake-on-LAN header. + */ +struct wol_hdr { + u8 wol_sync[6]; /* sync bytes */ + u8 wol_dest[16 * 6]; /* 16x destination MAC address */ + u8 wol_passwd[4]; /* optional password */ +}; + +/* + * Initialize wol (beginning of netloop) + */ +void wol_start(void); + +/* + * Deal with the receipt of a wol packet + * + * @param ip IP header in the packet + * @param len Packet length + */ +void wol_receive(struct ip_udp_hdr *ip, unsigned int len); + +/**********************************************************************/ + +#endif /* __WOL_H__ */ +#endif

This patch enables the WoL command
Signed-off-by: Lothar Felten lothar.felten@gmail.com --- cmd/Kconfig | 5 +++++ cmd/net.c | 14 ++++++++++++++ include/net.h | 3 ++- net/Makefile | 1 + net/net.c | 19 +++++++++++++++++++ 5 files changed, 41 insertions(+), 1 deletion(-)
diff --git a/cmd/Kconfig b/cmd/Kconfig index bc1d2f31c0..ed9d82fe71 100644 --- a/cmd/Kconfig +++ b/cmd/Kconfig @@ -1142,6 +1142,11 @@ config CMD_RARP help Boot image via network using RARP/TFTP protocol
+config CMD_WOL + bool "wol" + help + Wait for wake-on-lan packages + config CMD_NFS bool "nfs" default y diff --git a/cmd/net.c b/cmd/net.c index 67888d4e18..2e963b19c2 100644 --- a/cmd/net.c +++ b/cmd/net.c @@ -88,6 +88,20 @@ U_BOOT_CMD( ); #endif
+#if defined(CONFIG_CMD_WOL) +int do_wol(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) +{ + return netboot_common(WOL, cmdtp, argc, argv); +} + +U_BOOT_CMD( + wol, 2, 1, do_wol, + "wait for an incoming wake-on-lan packet", + "[timeout]\n" + "timeout is in seconds" +); +#endif + #if defined(CONFIG_CMD_DHCP) static int do_dhcp(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) { diff --git a/include/net.h b/include/net.h index 3469811aa0..0b4c2438a5 100644 --- a/include/net.h +++ b/include/net.h @@ -344,6 +344,7 @@ struct vlan_ethernet_hdr {
#define PROT_IP 0x0800 /* IP protocol */ #define PROT_ARP 0x0806 /* IP ARP protocol */ +#define PROT_WOL 0x0842 /* IP WOL protocol */ #define PROT_RARP 0x8035 /* IP ARP protocol */ #define PROT_VLAN 0x8100 /* IEEE 802.1q protocol */ #define PROT_IPV6 0x86dd /* IPv6 over bluebook */ @@ -535,7 +536,7 @@ 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 + TFTPSRV, TFTPPUT, LINKLOCAL, WOL };
extern char net_boot_file_name[1024];/* Boot File name */ diff --git a/net/Makefile b/net/Makefile index ce6e5adfa5..993b18f24c 100644 --- a/net/Makefile +++ b/net/Makefile @@ -25,6 +25,7 @@ obj-$(CONFIG_CMD_PING) += ping.o obj-$(CONFIG_CMD_RARP) += rarp.o obj-$(CONFIG_CMD_SNTP) += sntp.o obj-$(CONFIG_CMD_TFTPBOOT) += tftp.o +obj-$(CONFIG_CMD_WOL) += wol.o
# Disable this warning as it is triggered by: # sprintf(buf, index ? "foo%d" : "foo", index) diff --git a/net/net.c b/net/net.c index 8a9b69c6b0..e0088d352c 100644 --- a/net/net.c +++ b/net/net.c @@ -78,6 +78,12 @@ * - own IP address * We want: - network time * Next step: none + * + * WOL: + * + * Prerequisites: - own ethernet address + * We want: - magic packet to initiate action + * Next step: none */
@@ -107,6 +113,9 @@ #if defined(CONFIG_CMD_SNTP) #include "sntp.h" #endif +#if defined(CONFIG_CMD_WOL) +#include "wol.h" +#endif
DECLARE_GLOBAL_DATA_PTR;
@@ -508,6 +517,11 @@ restart: case LINKLOCAL: link_local_start(); break; +#endif +#if defined(CONFIG_CMD_WOL) + case WOL: + wol_start(); + break; #endif default: break; @@ -1274,6 +1288,11 @@ void net_process_received_packet(uchar *in_packet, int len) ntohs(ip->udp_src), ntohs(ip->udp_len) - UDP_HDR_SIZE); break; +#ifdef CONFIG_CMD_WOL + case PROT_WOL: + wol_receive(ip, len); + break; +#endif } }

Hi Lothar,
On 23 April 2018 at 11:47, Lothar Felten lothar.felten@gmail.com wrote:
This patch enables the WoL command
Signed-off-by: Lothar Felten lothar.felten@gmail.com
cmd/Kconfig | 5 +++++ cmd/net.c | 14 ++++++++++++++ include/net.h | 3 ++- net/Makefile | 1 + net/net.c | 19 +++++++++++++++++++ 5 files changed, 41 insertions(+), 1 deletion(-)
diff --git a/cmd/Kconfig b/cmd/Kconfig index bc1d2f31c0..ed9d82fe71 100644 --- a/cmd/Kconfig +++ b/cmd/Kconfig @@ -1142,6 +1142,11 @@ config CMD_RARP help Boot image via network using RARP/TFTP protocol
+config CMD_WOL
bool "wol"
help
Wait for wake-on-lan packages
Can you expand this a bit, perhaps explaining what it is for?
Regards, Simon

On Mon, Apr 23, 2018 at 12:47 PM, Lothar Felten lothar.felten@gmail.com wrote:
This patch enables the WoL command
Signed-off-by: Lothar Felten lothar.felten@gmail.com
cmd/Kconfig | 5 +++++ cmd/net.c | 14 ++++++++++++++ include/net.h | 3 ++- net/Makefile | 1 + net/net.c | 19 +++++++++++++++++++ 5 files changed, 41 insertions(+), 1 deletion(-)
diff --git a/cmd/Kconfig b/cmd/Kconfig index bc1d2f31c0..ed9d82fe71 100644 --- a/cmd/Kconfig +++ b/cmd/Kconfig @@ -1142,6 +1142,11 @@ config CMD_RARP help Boot image via network using RARP/TFTP protocol
+config CMD_WOL
This should not depend on CONFIG_CMD_NET... please move it outside of that guard. It does depend on CONFIG_NET, so make sure it stays in there. I would place it directly after CMD_PXE.
bool "wol"
help
Wait for wake-on-lan packages
packages -> Magic Packet
config CMD_NFS bool "nfs" default y diff --git a/cmd/net.c b/cmd/net.c index 67888d4e18..2e963b19c2 100644 --- a/cmd/net.c +++ b/cmd/net.c @@ -88,6 +88,20 @@ U_BOOT_CMD( ); #endif
+#if defined(CONFIG_CMD_WOL) +int do_wol(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) +{
return netboot_common(WOL, cmdtp, argc, argv);
You are passing the args into netboot_common, which interprets the first (single) parameter as a load address. That's clearly not what you want.
Instead of doing that, just parse out the timeout parameter in do_wol() and store it in a variable to be used by wol_start() and call net_loop(WOL);
If the net_loop returns < 0, then you should return CMD_RET_FAILURE, otherwise CMD_RET_SUCCESS.
+}
+U_BOOT_CMD(
wol, 2, 1, do_wol,
"wait for an incoming wake-on-lan packet",
"[timeout]\n"
The timeout should not be optional.
"timeout is in seconds"
+);
It seems that you are simply using cmd/net.c as a place to put this basically unrelated (to netboot) command. Please move this to a separate file (cmd/wol.c).
+#endif
#if defined(CONFIG_CMD_DHCP) static int do_dhcp(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) { diff --git a/include/net.h b/include/net.h index 3469811aa0..0b4c2438a5 100644 --- a/include/net.h +++ b/include/net.h @@ -344,6 +344,7 @@ struct vlan_ethernet_hdr {
#define PROT_IP 0x0800 /* IP protocol */ #define PROT_ARP 0x0806 /* IP ARP protocol */ +#define PROT_WOL 0x0842 /* IP WOL protocol */
Technically this comment should say "ether-wake WoL protocol"
#define PROT_RARP 0x8035 /* IP ARP protocol */ #define PROT_VLAN 0x8100 /* IEEE 802.1q protocol */ #define PROT_IPV6 0x86dd /* IPv6 over bluebook */ @@ -535,7 +536,7 @@ 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
TFTPSRV, TFTPPUT, LINKLOCAL, WOL
};
extern char net_boot_file_name[1024];/* Boot File name */ diff --git a/net/Makefile b/net/Makefile index ce6e5adfa5..993b18f24c 100644 --- a/net/Makefile +++ b/net/Makefile @@ -25,6 +25,7 @@ obj-$(CONFIG_CMD_PING) += ping.o obj-$(CONFIG_CMD_RARP) += rarp.o obj-$(CONFIG_CMD_SNTP) += sntp.o obj-$(CONFIG_CMD_TFTPBOOT) += tftp.o +obj-$(CONFIG_CMD_WOL) += wol.o
# Disable this warning as it is triggered by: # sprintf(buf, index ? "foo%d" : "foo", index) diff --git a/net/net.c b/net/net.c index 8a9b69c6b0..e0088d352c 100644 --- a/net/net.c +++ b/net/net.c @@ -78,6 +78,12 @@
- own IP address
We want: - network time
Next step: none
- WOL:
Prerequisites: - own ethernet address
We want: - magic packet to initiate action
*/
Next step: none
@@ -107,6 +113,9 @@ #if defined(CONFIG_CMD_SNTP) #include "sntp.h" #endif +#if defined(CONFIG_CMD_WOL) +#include "wol.h" +#endif
DECLARE_GLOBAL_DATA_PTR;
@@ -508,6 +517,11 @@ restart: case LINKLOCAL: link_local_start(); break; +#endif +#if defined(CONFIG_CMD_WOL)
case WOL:
wol_start();
break;
#endif default: break; @@ -1274,6 +1288,11 @@ void net_process_received_packet(uchar *in_packet, int len) ntohs(ip->udp_src), ntohs(ip->udp_len) - UDP_HDR_SIZE); break; +#ifdef CONFIG_CMD_WOL
case PROT_WOL:
WoL can also be sent as a UDP broadcast packet to port 0, 6, or, 7 or something (according to Wikipedia). This would match the wireshark dissector.
wol_receive(ip, len);
break;
+#endif } }
-- 2.14.1
U-Boot mailing list U-Boot@lists.denx.de https://lists.denx.de/listinfo/u-boot

Hi Lothar,
On 23 April 2018 at 11:47, Lothar Felten lothar.felten@gmail.com wrote:
This patch adds a new command 'wol': It waits for an incoming Wake-on-LAN packet or times out if no WoL packed is received. If the WoL packet contains a password, it is saved in the environment variable 'wolpassword' using the etherwake format (dot separated decimals).
Intended use case: a networked device should boot an alternate image. It's attached to a network on a client site, modifying the DHCP server configuration or setup of a tftp server is not allowed. After power on the device waits a few seconds for a WoL packet. If a packet is received, the device boots the alternate image. Otherwise it boots the default image.
This method is a simple way to interact with a system via network even if only the MAC address is known. Tools to send WoL packets are available on all common platforms.
Signed-off-by: Lothar Felten lothar.felten@gmail.com
net/wol.c | 54 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ net/wol.h | 40 ++++++++++++++++++++++++++++++++++++++++ 2 files changed, 94 insertions(+) create mode 100644 net/wol.c create mode 100644 net/wol.h
diff --git a/net/wol.c b/net/wol.c new file mode 100644 index 0000000000..0cedbaed85 --- /dev/null +++ b/net/wol.c @@ -0,0 +1,54 @@ +// SPDX-License-Identifier: GPL-2.0+ +/*
- Copyright 2018 Lothar Felten, lothar.felten@gmail.com
- */
+#include <common.h> +#include <command.h> +#include <net.h> +#include "wol.h"
+/*
Handle a received wake-on-lan packet.
- */
+void wol_receive(struct ip_udp_hdr *ip, unsigned int len) +{
char buf[4];
int i;
struct wol_hdr *wol;
wol = (struct wol_hdr *)ip;
if (len < 102)
return;
for (i = 0; i < 6; i++)
if (wol->wol_sync[i] != 0xff)
return;
for (i = 0; i < 16; i++)
if (memcmp(&wol->wol_dest[i * 6], net_ethaddr, 6) != 0)
return;
/* save the optional password using the etherwake format */
if (len >= 106) {
Is it possible to remove these four open-coded values and use values from the header file instead?
sprintf(buf, "%i.%i.%i.%i",
wol->wol_passwd[0], wol->wol_passwd[1],
wol->wol_passwd[2], wol->wol_passwd[3]);
env_set("wolpassword", buf);
}
net_set_state(NETLOOP_SUCCESS);
+}
+static void wol_timeout_handler(void) +{
eth_halt();
net_set_state(NETLOOP_FAIL);
+}
+void wol_start(void) +{
ulong timeout = env_get_ulong("woltimeout", 10, 5);
net_set_timeout_handler(timeout * 1000, wol_timeout_handler);
+} diff --git a/net/wol.h b/net/wol.h new file mode 100644 index 0000000000..e34767c733 --- /dev/null +++ b/net/wol.h @@ -0,0 +1,40 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/*
- Copyright 2018 Lothar Felten, lothar.felten@gmail.com
- */
+#if defined(CONFIG_CMD_WOL)
+#ifndef __WOL_H__ +#define __WOL_H__
+#include <net.h>
+/**********************************************************************/
+/*
Wake-on-LAN header.
- */
+struct wol_hdr {
u8 wol_sync[6]; /* sync bytes */
u8 wol_dest[16 * 6]; /* 16x destination MAC address */
u8 wol_passwd[4]; /* optional password */
+};
+/*
- Initialize wol (beginning of netloop)
- */
+void wol_start(void);
+/*
- Deal with the receipt of a wol packet
- @param ip IP header in the packet
- @param len Packet length
- */
+void wol_receive(struct ip_udp_hdr *ip, unsigned int len);
It seems like this should return an error code if the packet does not match as expected (the 'return' in your impl)
Also you could expand a bit on what 'deal with' means. What exactly happens?
Seems like there should be a README addition somewhere too.
+/**********************************************************************/
+#endif /* __WOL_H__ */
+#endif
2.14.1
Regards, Simon

Hi Lothar,
I generally like this. Thanks!
Please use the "net: " prefix to the subject of your patches. Also follow http://www.denx.de/wiki/U-Boot/Patches - Commit message conventions
" Use the imperative tense in your summary line (e.g., "Add support for X" rather than "Adds support for X"). In general, you can think of the summary line as "this commit is meant to 'Add support for X'" "
I'm not seeing any value in the way you've split up these two patches. It seems like they should be squashed into 1. I've commented in context in each patch.
On Mon, Apr 23, 2018 at 12:47 PM, Lothar Felten lothar.felten@gmail.com wrote:
This patch adds a new command 'wol': It waits for an incoming Wake-on-LAN packet or times out if no WoL packed is received. If the WoL packet contains a password, it is saved in the environment variable 'wolpassword' using the etherwake format (dot separated decimals).
Intended use case: a networked device should boot an alternate image. It's attached to a network on a client site, modifying the DHCP server configuration or setup of a tftp server is not allowed. After power on the device waits a few seconds for a WoL packet. If a packet is received, the device boots the alternate image. Otherwise it boots the default image.
This method is a simple way to interact with a system via network even if only the MAC address is known. Tools to send WoL packets are available on all common platforms.
This seems pretty good.
Please enable this on some board so that this is build-tested and provides others an example of how it can be used.
Signed-off-by: Lothar Felten lothar.felten@gmail.com
net/wol.c | 54 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ net/wol.h | 40 ++++++++++++++++++++++++++++++++++++++++ 2 files changed, 94 insertions(+) create mode 100644 net/wol.c create mode 100644 net/wol.h
diff --git a/net/wol.c b/net/wol.c new file mode 100644 index 0000000000..0cedbaed85 --- /dev/null +++ b/net/wol.c @@ -0,0 +1,54 @@ +// SPDX-License-Identifier: GPL-2.0+
Please use C-style comment.
+/*
- Copyright 2018 Lothar Felten, lothar.felten@gmail.com
- */
+#include <common.h> +#include <command.h> +#include <net.h> +#include "wol.h"
+/*
Handle a received wake-on-lan packet.
- */
+void wol_receive(struct ip_udp_hdr *ip, unsigned int len) +{
char buf[4];
This is broken. You write at least 8 bytes to this buffer and at most 16 bytes. Switching to ip_to_string() below will avoid this bug.
int i;
struct wol_hdr *wol;
wol = (struct wol_hdr *)ip;
if (len < 102)
Use sizeof(struct wol_hdr)
return;
for (i = 0; i < 6; i++)
Use #defines throughout.
if (wol->wol_sync[i] != 0xff)
return;
for (i = 0; i < 16; i++)
if (memcmp(&wol->wol_dest[i * 6], net_ethaddr, 6) != 0)
return;
/* save the optional password using the etherwake format */
etherwake -> ether-wake
if (len >= 106) {
The password is supposed to be 0, 4, or 6 bytes. You should check the actual length of the packet (instead of just "greater").
That would be "sizeof(struct wol_hdr) + ARP_PLEN" and "sizeof(struct wol_hdr) + ARP_HLEN"
sprintf(buf, "%i.%i.%i.%i",
wol->wol_passwd[0], wol->wol_passwd[1],
wol->wol_passwd[2], wol->wol_passwd[3]);
This sprintf should be replaced with ip_to_string().
env_set("wolpassword", buf);
You should encode the password as a MAC address if it is 6 bytes. For that you should use eth_env_set_enetaddr() instead of this.
}
net_set_state(NETLOOP_SUCCESS);
+}
+static void wol_timeout_handler(void) +{
eth_halt();
net_set_state(NETLOOP_FAIL);
+}
+void wol_start(void) +{
ulong timeout = env_get_ulong("woltimeout", 10, 5);
I think it would be nicer if this were a parameter to the wol command (as you have documented in the help for the command, but it doesn't actually do anything as is).
This function should also call net_set_udp_handler() for the UDP case. You'll probably need a static common function that does the meat of receive, and then a stub for UDP and a stub for ether-wake.
net_set_timeout_handler(timeout * 1000, wol_timeout_handler);
+} diff --git a/net/wol.h b/net/wol.h new file mode 100644 index 0000000000..e34767c733 --- /dev/null +++ b/net/wol.h @@ -0,0 +1,40 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/*
- Copyright 2018 Lothar Felten, lothar.felten@gmail.com
- */
+#if defined(CONFIG_CMD_WOL)
+#ifndef __WOL_H__ +#define __WOL_H__
+#include <net.h>
+/**********************************************************************/
+/*
Wake-on-LAN header.
- */
+struct wol_hdr {
u8 wol_sync[6]; /* sync bytes */
Make a #define for this sync size so that you can use it above in the implementation.
u8 wol_dest[16 * 6]; /* 16x destination MAC address */
Make a #define for the "16" that you can use in the implementation above and use ARP_HLEN for the MAC length.
u8 wol_passwd[4]; /* optional password */
Make this size 0 so you can get the size of this structure without a password. You only ever use a pointer to this structure anyway.
+};
+/*
- Initialize wol (beginning of netloop)
- */
+void wol_start(void);
+/*
- Deal with the receipt of a wol packet
- @param ip IP header in the packet
- @param len Packet length
- */
+void wol_receive(struct ip_udp_hdr *ip, unsigned int len);
You should add a function here that you can use to set the timeout. Then in the net/wol.c you can implement it and write it to a static global variable.
+/**********************************************************************/
+#endif /* __WOL_H__ */
+#endif
2.14.1
U-Boot mailing list U-Boot@lists.denx.de https://lists.denx.de/listinfo/u-boot
participants (3)
-
Joe Hershberger
-
Lothar Felten
-
Simon Glass