u-boot/net/wol.c
Lothar Felten d8970dae27 net: Add new wol command - Wake on LAN
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>
2018-07-02 14:14:20 -05:00

97 lines
2.0 KiB
C

// 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);
}