1// SPDX-License-Identifier: GPL-2.0+
2/*
3 * Copyright 2018 Lothar Felten, lothar.felten@gmail.com
4 */
5
6#include <common.h>
7#include <command.h>
8#include <env.h>
9#include <net.h>
10#include "wol.h"
11
12static ulong wol_timeout = WOL_DEFAULT_TIMEOUT;
13
14/*
15 * Check incoming Wake-on-LAN packet for:
16 * - sync bytes
17 * - sixteen copies of the target MAC address
18 *
19 * @param wol Wake-on-LAN packet
20 * @param len Packet length
21 */
22static int wol_check_magic(struct wol_hdr *wol, unsigned int len)
23{
24	int i;
25
26	if (len < sizeof(struct wol_hdr))
27		return 0;
28
29	for (i = 0; i < WOL_SYNC_COUNT; i++)
30		if (wol->wol_sync[i] != WOL_SYNC_BYTE)
31			return 0;
32
33	for (i = 0; i < WOL_MAC_REPETITIONS; i++)
34		if (memcmp(&wol->wol_dest[i * ARP_HLEN],
35			   net_ethaddr, ARP_HLEN) != 0)
36			return 0;
37
38	return 1;
39}
40
41void wol_receive(struct ip_udp_hdr *ip, unsigned int len)
42{
43	struct wol_hdr *wol;
44
45	wol = (struct wol_hdr *)ip;
46
47	if (!wol_check_magic(wol, len))
48		return;
49
50	/* save the optional password using the ether-wake formats */
51	/* don't check for exact length, the packet might have padding */
52	if (len >= (sizeof(struct wol_hdr) + WOL_PASSWORD_6B)) {
53		eth_env_set_enetaddr("wolpassword", wol->wol_passwd);
54	} else if (len >= (sizeof(struct wol_hdr) + WOL_PASSWORD_4B)) {
55		char buffer[16];
56		struct in_addr *ip = (struct in_addr *)(wol->wol_passwd);
57
58		ip_to_string(*ip, buffer);
59		env_set("wolpassword", buffer);
60	}
61	net_set_state(NETLOOP_SUCCESS);
62}
63
64static void wol_udp_handler(uchar *pkt, unsigned int dest, struct in_addr sip,
65			    unsigned int src, unsigned int len)
66{
67	struct wol_hdr *wol;
68
69	wol = (struct wol_hdr *)pkt;
70
71	/* UDP destination port must be 0, 7 or 9 */
72	if (dest != 0 && dest != 7 && dest != 9)
73		return;
74
75	if (!wol_check_magic(wol, len))
76		return;
77
78	net_set_state(NETLOOP_SUCCESS);
79}
80
81void wol_set_timeout(ulong timeout)
82{
83	wol_timeout = timeout;
84}
85
86static void wol_timeout_handler(void)
87{
88	eth_halt();
89	net_set_state(NETLOOP_FAIL);
90}
91
92void wol_start(void)
93{
94	net_set_timeout_handler(wol_timeout, wol_timeout_handler);
95	net_set_udp_handler(wol_udp_handler);
96}
97