net: Add new wol command - Wake on LAN
authorLothar Felten <lothar.felten@gmail.com>
Fri, 22 Jun 2018 20:29:54 +0000 (22:29 +0200)
committerJoe Hershberger <joe.hershberger@ni.com>
Mon, 2 Jul 2018 19:14:20 +0000 (14:14 -0500)
Add a new command 'wol': Wait for an incoming Wake-on-LAN packet or
time 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 or colon
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.

Some Ethernet drivers seem to pad the incoming packet. The additional
padding bytes might be recognized as Wake-on-LAN password bytes.

By default enabled in pengwyn_defconfig.

Signed-off-by: Lothar Felten <lothar.felten@gmail.com>
Acked-by: Joe Hershberger <joe.hershberger@ni.com>
cmd/Kconfig
cmd/Makefile
cmd/wol.c [new file with mode: 0644]
configs/pengwyn_defconfig
include/net.h
net/Makefile
net/net.c
net/wol.c [new file with mode: 0644]
net/wol.h [new file with mode: 0644]

index 45c83359add828a60dd8e6f69f9b4abb39a2e44d..bbf9fc9113053bc74bc59fffef09292035a8df76 100644 (file)
@@ -1239,6 +1239,11 @@ config CMD_PXE
        help
          Boot image via network using PXE protocol
 
+config CMD_WOL
+       bool "wol"
+       help
+         Wait for wake-on-lan Magic Packet
+
 endif
 
 menu "Misc commands"
index 13cf7bf6c205d43a42214f4d44bde4205c661386..323f1fd2c77f210770f93eabd1411cb674cfcc03 100644 (file)
@@ -100,6 +100,7 @@ obj-$(CONFIG_CMD_PCI) += pci.o
 endif
 obj-y += pcmcia.o
 obj-$(CONFIG_CMD_PXE) += pxe.o
+obj-$(CONFIG_CMD_WOL) += wol.o
 obj-$(CONFIG_CMD_QFW) += qfw.o
 obj-$(CONFIG_CMD_READ) += read.o
 obj-$(CONFIG_CMD_REGINFO) += reginfo.o
diff --git a/cmd/wol.c b/cmd/wol.c
new file mode 100644 (file)
index 0000000..8a756f3
--- /dev/null
+++ b/cmd/wol.c
@@ -0,0 +1,33 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * (C) Copyright 2018
+ * Lothar Felte, lothar.felten@gmail.com
+ */
+
+/*
+ * Wake-on-LAN support
+ */
+#include <common.h>
+#include <command.h>
+#include <net.h>
+
+#if defined(CONFIG_CMD_WOL)
+void wol_set_timeout(ulong);
+
+int do_wol(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])
+{
+       /* Validate arguments */
+       if (argc < 2)
+               return CMD_RET_USAGE;
+       wol_set_timeout(simple_strtol(argv[1], NULL, 10) * 1000);
+       if (net_loop(WOL) < 0)
+               return CMD_RET_FAILURE;
+       return CMD_RET_SUCCESS;
+}
+
+U_BOOT_CMD(
+       wol,    2,      1,      do_wol,
+       "wait for an incoming wake-on-lan packet",
+       "Timeout"
+);
+#endif
index 0ee8e6e1b982de49a5a0f560f56a01ccf59c19fe..76b5715a9904bfd992899d7027f822156c863952 100644 (file)
@@ -37,6 +37,7 @@ CONFIG_CMD_MMC=y
 CONFIG_CMD_NAND=y
 CONFIG_CMD_SPI=y
 CONFIG_CMD_USB=y
+CONFIG_CMD_WOL=y
 # CONFIG_CMD_SETEXPR is not set
 CONFIG_CMD_EXT4_WRITE=y
 CONFIG_CMD_MTDPARTS=y
index 57606855564f002f5b0d4e38c1df926a998e0a43..9c7199aca92c05e41b3c2a14882910d9ea35eced 100644 (file)
@@ -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          /* 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, FASTBOOT
+       TFTPSRV, TFTPPUT, LINKLOCAL, FASTBOOT, WOL
 };
 
 extern char    net_boot_file_name[1024];/* Boot File name */
index 07466879f501f77aea77853b9f2ba609f7ad64a7..ce36362168509cf423155d7a972d44acaf1903c7 100644 (file)
@@ -24,6 +24,7 @@ obj-$(CONFIG_CMD_RARP) += rarp.o
 obj-$(CONFIG_CMD_SNTP) += sntp.o
 obj-$(CONFIG_CMD_TFTPBOOT) += tftp.o
 obj-$(CONFIG_UDP_FUNCTION_FASTBOOT)  += fastboot.o
+obj-$(CONFIG_CMD_WOL)  += wol.o
 
 # Disable this warning as it is triggered by:
 # sprintf(buf, index ? "foo%d" : "foo", index)
index b4563a4cab1fbfbccf595ee40a5e865514d79884..084269e31ecf65bad2aa190c075d755be928cef8 100644 (file)
--- a/net/net.c
+++ b/net/net.c
  *                     - own IP address
  *     We want:        - network time
  *     Next step:      none
+ *
+ * WOL:
+ *
+ *     Prerequisites:  - own ethernet address
+ *     We want:        - magic packet or timeout
+ *     Next step:      none
  */
 
 
 #if defined(CONFIG_CMD_SNTP)
 #include "sntp.h"
 #endif
+#if defined(CONFIG_CMD_WOL)
+#include "wol.h"
+#endif
 
 /** BOOTP EXTENTIONS **/
 
@@ -514,6 +523,11 @@ restart:
                case LINKLOCAL:
                        link_local_start();
                        break;
+#endif
+#if defined(CONFIG_CMD_WOL)
+               case WOL:
+                       wol_start();
+                       break;
 #endif
                default:
                        break;
@@ -1281,6 +1295,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
        }
 }
 
diff --git a/net/wol.c b/net/wol.c
new file mode 100644 (file)
index 0000000..946bd91
--- /dev/null
+++ b/net/wol.c
@@ -0,0 +1,96 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright 2018 Lothar Felten, lothar.felten@gmail.com
+ */
+
+#include <common.h>
+#include <command.h>
+#include <net.h>
+#include <environment.h>
+#include "wol.h"
+
+static ulong wol_timeout = WOL_DEFAULT_TIMEOUT;
+
+/*
+ * Check incoming Wake-on-LAN packet for:
+ * - sync bytes
+ * - sixteen copies of the target MAC address
+ *
+ * @param wol Wake-on-LAN packet
+ * @param len Packet length
+ */
+static int wol_check_magic(struct wol_hdr *wol, unsigned int len)
+{
+       int i;
+
+       if (len < sizeof(struct wol_hdr))
+               return 0;
+
+       for (i = 0; i < WOL_SYNC_COUNT; i++)
+               if (wol->wol_sync[i] != WOL_SYNC_BYTE)
+                       return 0;
+
+       for (i = 0; i < WOL_MAC_REPETITIONS; i++)
+               if (memcmp(&wol->wol_dest[i * ARP_HLEN],
+                          net_ethaddr, ARP_HLEN) != 0)
+                       return 0;
+
+       return 1;
+}
+
+void wol_receive(struct ip_udp_hdr *ip, unsigned int len)
+{
+       struct wol_hdr *wol;
+
+       wol = (struct wol_hdr *)ip;
+
+       if (!wol_check_magic(wol, len))
+               return;
+
+       /* save the optional password using the ether-wake formats */
+       /* don't check for exact length, the packet might have padding */
+       if (len >= (sizeof(struct wol_hdr) + WOL_PASSWORD_6B)) {
+               eth_env_set_enetaddr("wolpassword", wol->wol_passwd);
+       } else if (len >= (sizeof(struct wol_hdr) + WOL_PASSWORD_4B)) {
+               char buffer[16];
+               struct in_addr *ip = (struct in_addr *)(wol->wol_passwd);
+
+               ip_to_string(*ip, buffer);
+               env_set("wolpassword", buffer);
+       }
+       net_set_state(NETLOOP_SUCCESS);
+}
+
+static void wol_udp_handler(uchar *pkt, unsigned int dest, struct in_addr sip,
+                           unsigned int src, unsigned int len)
+{
+       struct wol_hdr *wol;
+
+       wol = (struct wol_hdr *)pkt;
+
+       /* UDP destination port must be 0, 7 or 9 */
+       if (dest != 0 && dest != 7 && dest != 9)
+               return;
+
+       if (!wol_check_magic(wol, len))
+               return;
+
+       net_set_state(NETLOOP_SUCCESS);
+}
+
+void wol_set_timeout(ulong timeout)
+{
+       wol_timeout = timeout;
+}
+
+static void wol_timeout_handler(void)
+{
+       eth_halt();
+       net_set_state(NETLOOP_FAIL);
+}
+
+void wol_start(void)
+{
+       net_set_timeout_handler(wol_timeout, wol_timeout_handler);
+       net_set_udp_handler(wol_udp_handler);
+}
diff --git a/net/wol.h b/net/wol.h
new file mode 100644 (file)
index 0000000..ebc81f2
--- /dev/null
+++ b/net/wol.h
@@ -0,0 +1,65 @@
+/* SPDX-License-Identifier: GPL-2.0+ */
+/*
+ * wol - Wake-on-LAN
+ *
+ * Supports both Wake-on-LAN packet types:
+ * - EtherType 0x0842 packets
+ * - UDP packets on ports 0, 7 and 9.
+ *
+ * Copyright 2018 Lothar Felten, lothar.felten@gmail.com
+ */
+
+#if defined(CONFIG_CMD_WOL)
+
+#ifndef __WOL_H__
+#define __WOL_H__
+
+#include <net.h>
+
+/**********************************************************************/
+
+#define WOL_SYNC_BYTE                  0xFF
+#define WOL_SYNC_COUNT                 6
+#define WOL_MAC_REPETITIONS            16
+#define WOL_DEFAULT_TIMEOUT            5000
+#define WOL_PASSWORD_4B                        4
+#define WOL_PASSWORD_6B                        6
+
+/*
+ * Wake-on-LAN header
+ */
+struct wol_hdr {
+       u8      wol_sync[WOL_SYNC_COUNT];                       /* sync bytes */
+       u8      wol_dest[WOL_MAC_REPETITIONS * ARP_HLEN];       /* 16x MAC */
+       u8      wol_passwd[0];                                  /* optional */
+};
+
+/*
+ * Initialize wol (beginning of netloop)
+ */
+void wol_start(void);
+
+/*
+ * Check incoming Wake-on-LAN packet for:
+ * - sync bytes
+ * - sixteen copies of the target MAC address
+ *
+ * Optionally store the four or six byte password in the environment
+ * variable "wolpassword"
+ *
+ * @param ip IP header in the packet
+ * @param len Packet length
+ */
+void wol_receive(struct ip_udp_hdr *ip, unsigned int len);
+
+/*
+ * Set the timeout for the reception of a Wake-on-LAN packet
+ *
+ * @param timeout in milliseconds
+ */
+void wol_set_timeout(ulong timeout);
+
+/**********************************************************************/
+
+#endif /* __WOL_H__ */
+#endif