I'm often torn between the desire to switch my PC off when I'm not using it as an attempt to perform some token gesture towards being green (every little helps) and the desire to leave it on so that I can access it remotely from where ever I happen to be.
Wouldn't it be handy if you could allow your PC to sleep automatically but somehow have the ability to wake it up without having to push the power button in the rare event that I would like to use it when I'm nowhere near it? Turns out there is - however it isn't new, it's just only recently that I've been bothered/driven to investigate this (delete as appropriate) and have been pleasantly surprised as to how easy it is to make it work and how reliably it seems to work.
Most PCs with modern motherboards support Wake On LAN, which is why when your PC goes to standby or hibernates there is still a link light on the network card - it is still awake and waiting for packet to arrive which would require it to signal the PC to wake. The packet it's looking out for is known as the magic packet and is specially laid to make it easy for the little brain on board the network card to spot it while the CPU and rest of the PC is turned off.
The Magic Packet is 102 bytes long made up of a 6 byte header of all 0xFF followed by the target computer's MAC address sixteen times (6 bytes each) and is sent out using UDP.
We can build and send one of these from C# reasonably easily using the UdpClient class:
/// <summary>
/// Send a magic packet
/// </summary>
/// <param name="macAddress"></param>
public static void Wake(byte[] macAddress)
{
// A Wake on LAN magic packet contains a 6 byte header and
// the MAC address of the target MAC address (6 bytes) 16 times
byte[] wolPacket = new byte[17 * 6];
MemoryStream ms = new MemoryStream(wolPacket, true);
// Write the 6 byte 0xFF header
for (int i = 0; i < 6; i++)
{
ms.WriteByte(0xFF);
}
// Write the MAC Address 16 times
for (int i = 0; i < 16; i++)
{
ms.Write(macAddress, 0, macAddress.Length);
}
// Broadcast the magic packet
UdpClient udp = new UdpClient();
udp.Connect(IPAddress.Broadcast, 0);
udp.Send(wolPacket, wolPacket.Length);
}
Note, by using a UDP Broadcast the sender and recipient must be on the same subnet, otherwise you'll have to teach your router what's going on.
This then leads to the question of how to obtain the MAC address of the remote computer. If you are sat in front of it you can run ipconfig /all at a command line to get the address:
Ethernet adapter Local Area Connection:
Connection-specific DNS Suffix . : imeta.co.uk
Description . . . . . . . . . . . : Broadcom NetXtreme 57xx Gigabit Controller
Physical Address. . . . . . . . . : 00:1C:23:27:CE:C0
DHCP Enabled. . . . . . . . . . . : Yes
Autoconfiguration Enabled . . . . : Yes
Link-local IPv6 Address . . . . . : ... etc
|
Now we have a string with the MAC address we can wake that machine up from someplace else:
public static void Wake(string macAddress)
{
Wake ( macAddress.Split(':').Select(c => byte.Parse(c, NumberStyles.HexNumber)).ToArray() );
}
However, if I don't want to have to run a command on the local computer to wake it up remotely I can ask it over the network what its MAC address is using a protocol called ARP. ARP is used to find out which MAC address currently owns a particular IP address.
So given the I know the name of the remote PC, I'd like to be able to discover the MAC address and wake it up. Unfortunately there is no managed class for generating and sending ARP requests, we need to make use of a native DLL:
internal static class NativeMethods
{
[DllImport("iphlpapi.dll", ExactSpelling = true)]
internal static extern int SendARP(int DestIP, int SrcIP, byte[] pMacAddr, ref uint PhyAddrLen);
}
/// <summary>
/// Summary description for ARP
/// </summary>
public static class Arp
{
public static byte[] GetMACAddress(string hostNameOrAddress)
{
IPHostEntry hostEntry = Dns.GetHostEntry(hostNameOrAddress);
byte[] macAddr = new byte[6];
uint macAddrLen = (uint)macAddr.Length;
// Find the first IPV4 address for that host
IPAddress ipAddress = hostEntry.AddressList.First<IPAddress>(ip => ip.AddressFamily == System.Net.Sockets.AddressFamily.InterNetwork);
byte[] addressBytes = ipAddress.GetAddressBytes();
int address = BitConverter.ToInt32(addressBytes, 0);
if (NativeMethods.SendARP(BitConverter.ToInt32(addressBytes, 0), 0, macAddr, ref macAddrLen) != 0)
{
return null;
}
return addressBytes;
}
}
Great, so now given the name of a PC we can use DNS to lookup it's IP address, send an ARP request to obtain its MAC address and then wake up the remote computer using a Magic Packet? Sorted? No... ARP tells you which MAC address currently owns an IP address, when a computer is in standby it doesn't have an IP address - so it doesn't need to respond to ARP requests.
While understandable, this is a real shame as it means if a remote computer is asleep there is no means of obtaining it's MAC address; you can only find a computers MAC address if the computer is on!
In order to produce a useful system to wake up PCs when they're asleep the system needs to know ahead of time which machines I'm likely to want to wake. By creating a little ASP.NET application we can register the machines that are likely to sleep while they're awake and store the MAC address so that we have it handy when it's asleep. By placing this application on a central server everyone can register their computers on it and there is one central place to visit to turn them back on again:
Clicking the add computer button will use the hostname string that I've entered, obtain the MAC address for it and squirrel it away into a database. If the remote machine isn't on at the moment it can't be added to the collection until it is. However once my PC has been added we can then view the machines I've added and wake any machines of mine that are asleep:
By clicking on the "Wake up" link, the asp.net page can use the MAC address stored in the database to create a magic packet and wake the PC up. We can also do some little touches like pinging the machine as the page loads and tailoring the icons and links available; there's no point waking a machine that's awake!
When my computer is awake it shows up as online:
So now are we finished? Almost! The web application now has everything it needs to make it work, however if you try it the remote PC probably won't wake up, as Wake-On-LAN is not enabled by default! You need to enable it in your BIOS and in Windows.
In the BIOS it will probably be under power management or similar, like this example:
... and then the driver for the network card needs to be taught what's going on. Locate your network card under Device Manager, and click properties and Power Management and then check "Allow this device to wake the computer"
And finally, it seem to help if you tell the network card to expect a Magic Packet as the wake-up signal rather than a ping or any other type of packet:
And now we're done! Your PC should now wake when instructed to by a magic packet!
So that was a lot of heart-ache, why bother? A quick burst of maths demonstrates the scale of the cost savings that are possible through a bit of green IT: Working on the assumption that an idle desktop computer pulls 150w at idle, it means that a powered on PC will burn 1 KWh every 6 hours 40 minutes. For a PC on 24/7 that's 25.2 KWh per week or 1,310 KWh per year. Assuming a rough price of 10p per KWh a single PC costs £131 per year to run 24/7. Now how many PC's do you have around the place? Two? Five? Maybe a hundred (£13,100 per year) - a thousand maybe?
By making the PC's switch to standby the figures change, let's say each one is on 9AM till 6PM, 5 days a week, 48 weeks of the year (people go on hols!). The PC is on for 9 hours a day, 2,160 hours per year which consumes 324KWh, which costs £32.40. A saving of just shy of a hundred pounds per PC per year - assuming the cost of electricity remains constant, and that's probably the worst assumption I've made so far! As the cost of energy goes up, the savings will increase proportionally too.
It's probably worth factoring in the amount of time that the PC is on standby into the equation, as they are still pulling some current: if the PC is on for 2,160 hours per year, it is off or on standby for 4,200. Assuming a draw of 5w in that state it will consume 21KWh over the year - costing £2. I think the case for the savings still holds!
At iMeta we have this live now on our intranet site (http://intranet/wake for iMeta people) but it's probably something that anyone who leaves a desktop PC on 24/7 should consider setting up.
Credits: Thanks go to Bart and Lukas that got me down the road to this solution!
26/11/08 Update: This solution and the source are available to download