cvsdist 2b639f3
/* ether-wake.c: Send a magic packet to wake up sleeping machines. */
cvsdist 2b639f3
cvsdist 2b639f3
static char version_msg[] =
7ab46c3
"ether-wake.c: v1.09 11/12/2003 Donald Becker, http://www.scyld.com/";
cvsdist 2b639f3
static char brief_usage_msg[] =
Jaromír Končický 0373077
"usage: ether-wake [-i <ifname>] [-p aa:bb:cc:dd[:ee:ff]] 00:11:22:33:44:55\n"
7ab46c3
"   Use '-u' to see the complete set of options.\n";
cvsdist 2b639f3
static char usage_msg[] =
Jaromír Končický 0373077
"usage: ether-wake [-i <ifname>] [-p aa:bb:cc:dd[:ee:ff]] 00:11:22:33:44:55\n"
ebeb31b
"\n"
ebeb31b
"	This program generates and transmits a Wake-On-LAN (WOL)\n"
ebeb31b
"	\"Magic Packet\", used for restarting machines that have been\n"
ebeb31b
"	soft-powered-down (ACPI D3-warm state).\n"
ebeb31b
"	It currently generates the standard AMD Magic Packet format, with\n"
ebeb31b
"	an optional password appended.\n"
ebeb31b
"\n"
ebeb31b
"	The single required parameter is the Ethernet MAC (station) address\n"
ebeb31b
"	of the machine to wake or a host ID with known NSS 'ethers' entry.\n"
ebeb31b
"	The MAC address may be found with the 'arp' program while the target\n"
ebeb31b
"	machine is awake.\n"
ebeb31b
"\n"
ebeb31b
"	Options:\n"
ebeb31b
"		-b	Send wake-up packet to the broadcast address.\n"
ebeb31b
"		-D	Increase the debug level.\n"
Jaromír Končický 0373077
"		-i ifname	Use interface IFNAME instead of the default 'eth0'.\n"
ebeb31b
"		-p <pw>		Append the four or six byte password PW to the packet.\n"
ebeb31b
"					A password is only required for a few adapter types.\n"
ebeb31b
"					The password may be specified in ethernet hex format\n"
ebeb31b
"					or dotted decimal (Internet address)\n"
ebeb31b
"		-p 00:22:44:66:88:aa\n"
ebeb31b
"		-p 192.168.1.1\n";
cvsdist 2b639f3
cvsdist 2b639f3
/*
cvsdist 2b639f3
	This program generates and transmits a Wake-On-LAN (WOL) "Magic Packet",
cvsdist 2b639f3
	used for restarting machines that have been soft-powered-down
cvsdist 2b639f3
	(ACPI D3-warm state).  It currently generates the standard AMD Magic Packet
cvsdist 2b639f3
	format, with an optional password appended.
cvsdist 2b639f3
cvsdist 2b639f3
	This software may be used and distributed according to the terms
cvsdist 2b639f3
	of the GNU Public License, incorporated herein by reference.
cvsdist 2b639f3
	Contact the author for use under other terms.
cvsdist 2b639f3
ebeb31b
	This source file was originally part of the network tricks package, and
ebeb31b
	is now distributed to support the Scyld Beowulf system.
ebeb31b
	Copyright 1999-2003 Donald Becker and Scyld Computing Corporation.
cvsdist 2b639f3
cvsdist 2b639f3
	The author may be reached as becker@scyld, or C/O
cvsdist 2b639f3
	 Scyld Computing Corporation
7ab46c3
	 914 Bay Ridge Road, Suite 220
cvsdist 2b639f3
	 Annapolis MD 21403
cvsdist 2b639f3
ebeb31b
  Notes:
ebeb31b
  On some systems dropping root capability allows the process to be
cvsdist 2b639f3
  dumped, traced or debugged.
cvsdist 2b639f3
  If someone traces this program, they get control of a raw socket.
cvsdist 2b639f3
  Linux handles this safely, but beware when porting this program.
cvsdist 2b639f3
ebeb31b
  An alternative to needing 'root' is using a UDP broadcast socket, however
ebeb31b
  doing so only works with adapters configured for unicast+broadcast Rx
ebeb31b
  filter.  That configuration consumes more power.
cvsdist 2b639f3
*/
7ab46c3

cvsdist 2b639f3
#include <unistd.h>
cvsdist 2b639f3
#include <stdlib.h>
cvsdist 2b639f3
#include <stdio.h>
cvsdist 2b639f3
#include <errno.h>
cvsdist 2b639f3
#include <ctype.h>
cvsdist 2b639f3
#include <string.h>
cvsdist 2b639f3
cvsdist 2b639f3
#if 0							/* Only exists on some versions. */
cvsdist 2b639f3
#include <ioctls.h>
cvsdist 2b639f3
#endif
cvsdist 2b639f3
cvsdist 2b639f3
#include <sys/socket.h>
cvsdist 2b639f3
cvsdist 2b639f3
#include <sys/types.h>
cvsdist 2b639f3
#include <sys/ioctl.h>
cvsdist 2b639f3
#include <linux/if.h>
cvsdist 2b639f3
ebeb31b
#include <features.h>
ebeb31b
#if __GLIBC__ >= 2 && __GLIBC_MINOR >= 1
ebeb31b
#include <netpacket/packet.h>
ebeb31b
#include <net/ethernet.h>
ebeb31b
#else
ebeb31b
#include <asm/types.h>
ebeb31b
#include <linux/if_packet.h>
ebeb31b
#include <linux/if_ether.h>
ebeb31b
#endif
ebeb31b
#include <netdb.h>
ebeb31b
#include <netinet/ether.h>
ebeb31b
ebeb31b
/* Grrr, no consistency between include versions.
ebeb31b
   Enable this if setsockopt() isn't declared with your library. */
ebeb31b
#if 0
ebeb31b
extern int setsockopt __P ((int __fd, int __level, int __optname,
ebeb31b
							__ptr_t __optval, int __optlen));
cvsdist 2b639f3
#else				/* New, correct head files.  */
cvsdist 2b639f3
#include <sys/socket.h>
cvsdist 2b639f3
#endif
cvsdist 2b639f3
cvsdist 2b639f3
u_char outpack[1000];
cvsdist 2b639f3
int outpack_sz = 0;
cvsdist 2b639f3
int debug = 0;
cvsdist 2b639f3
u_char wol_passwd[6];
cvsdist 2b639f3
int wol_passwd_sz = 0;
cvsdist 2b639f3
cvsdist 2b639f3
static int opt_no_src_addr = 0, opt_broadcast = 0;
cvsdist 2b639f3
ebeb31b
static int get_dest_addr(const char *arg, struct ether_addr *eaddr);
ebeb31b
static int get_fill(unsigned char *pkt, struct ether_addr *eaddr);
cvsdist 2b639f3
static int get_wol_pw(const char *optarg);
cvsdist 2b639f3
cvsdist 2b639f3
int main(int argc, char *argv[])
cvsdist 2b639f3
{
Jaromír Končický 0373077
	char *ifname = "eth0";
cvsdist 2b639f3
	int one = 1;				/* True, for socket options. */
cvsdist 2b639f3
	int s;						/* Raw socket */
cvsdist 2b639f3
	int errflag = 0, verbose = 0, do_version = 0;
ebeb31b
	int perm_failure = 0;
cvsdist 2b639f3
	int i, c, pktsize;
ebeb31b
#if defined(PF_PACKET)
ebeb31b
	struct sockaddr_ll whereto;
ebeb31b
#else
ebeb31b
	struct sockaddr whereto;	/* who to wake up */
ebeb31b
#endif
ebeb31b
	struct ether_addr eaddr;
cvsdist 2b639f3
cvsdist 2b639f3
	while ((c = getopt(argc, argv, "bDi:p:uvV")) != -1)
cvsdist 2b639f3
		switch (c) {
cvsdist 2b639f3
		case 'b': opt_broadcast++;	break;
cvsdist 2b639f3
		case 'D': debug++;			break;
cvsdist 2b639f3
		case 'i': ifname = optarg;	break;
cvsdist 2b639f3
		case 'p': get_wol_pw(optarg); break;
cvsdist 2b639f3
		case 'u': printf(usage_msg); return 0;
cvsdist 2b639f3
		case 'v': verbose++;		break;
cvsdist 2b639f3
		case 'V': do_version++;		break;
cvsdist 2b639f3
		case '?':
cvsdist 2b639f3
			errflag++;
cvsdist 2b639f3
		}
cvsdist 2b639f3
	if (verbose || do_version)
cvsdist 2b639f3
		printf("%s\n", version_msg);
cvsdist 2b639f3
	if (errflag) {
cvsdist 2b639f3
		fprintf(stderr, brief_usage_msg);
cvsdist 2b639f3
		return 3;
cvsdist 2b639f3
	}
cvsdist 2b639f3
cvsdist 2b639f3
	if (optind == argc) {
cvsdist 2b639f3
		fprintf(stderr, "Specify the Ethernet address as 00:11:22:33:44:55.\n");
cvsdist 2b639f3
		return 3;
cvsdist 2b639f3
	}
cvsdist 2b639f3
cvsdist 2b639f3
	/* Note: PF_INET, SOCK_DGRAM, IPPROTO_UDP would allow SIOCGIFHWADDR to
cvsdist 2b639f3
	   work as non-root, but we need SOCK_PACKET to specify the Ethernet
cvsdist 2b639f3
	   destination address. */
ebeb31b
#if defined(PF_PACKET)
ebeb31b
	s = socket(PF_PACKET, SOCK_RAW, 0);
ebeb31b
#else
ebeb31b
	s = socket(AF_INET, SOCK_PACKET, SOCK_PACKET);
ebeb31b
#endif
ebeb31b
	if (s < 0) {
cvsdist 2b639f3
		if (errno == EPERM)
7ab46c3
			fprintf(stderr, "ether-wake: This program must be run as root.\n");
cvsdist 2b639f3
		else
cvsdist 2b639f3
			perror("ether-wake: socket");
ebeb31b
		perm_failure++;
cvsdist 2b639f3
	}
cvsdist 2b639f3
	/* Don't revert if debugging allows a normal user to get the raw socket. */
cvsdist 2b639f3
	setuid(getuid());
cvsdist 2b639f3
ebeb31b
	/* We look up the station address before reporting failure so that
ebeb31b
	   errors may be reported even when run as a normal user.
ebeb31b
	*/
ebeb31b
	if (get_dest_addr(argv[optind], &eaddr) != 0)
ebeb31b
		return 3;
ebeb31b
	if (perm_failure && ! debug)
ebeb31b
		return 2;
ebeb31b
ebeb31b
	pktsize = get_fill(outpack, &eaddr);
cvsdist 2b639f3
cvsdist 2b639f3
	/* Fill in the source address, if possible.
cvsdist 2b639f3
	   The code to retrieve the local station address is Linux specific. */
ebeb31b
	if (! opt_no_src_addr) {
cvsdist 2b639f3
		struct ifreq if_hwaddr;
cvsdist 2b639f3
		unsigned char *hwaddr = if_hwaddr.ifr_hwaddr.sa_data;
cvsdist 2b639f3
Jaromír Končický 0373077
		strcpy(if_hwaddr.ifr_name, ifname);
cvsdist 2b639f3
		if (ioctl(s, SIOCGIFHWADDR, &if_hwaddr) < 0) {
cvsdist 2b639f3
			fprintf(stderr, "SIOCGIFHWADDR on %s failed: %s\n", ifname,
cvsdist 2b639f3
					strerror(errno));
ebeb31b
			/* Magic packets still work if our source address is bogus, but
ebeb31b
			   we fail just to be anal. */
cvsdist 2b639f3
			return 1;
cvsdist 2b639f3
		}
cvsdist 2b639f3
		memcpy(outpack+6, if_hwaddr.ifr_hwaddr.sa_data, 6);
cvsdist 2b639f3
cvsdist 2b639f3
		if (verbose) {
cvsdist 2b639f3
			printf("The hardware address (SIOCGIFHWADDR) of %s is type %d  "
cvsdist 2b639f3
				   "%2.2x:%2.2x:%2.2x:%2.2x:%2.2x:%2.2x.\n", ifname,
cvsdist 2b639f3
				   if_hwaddr.ifr_hwaddr.sa_family, hwaddr[0], hwaddr[1],
cvsdist 2b639f3
				   hwaddr[2], hwaddr[3], hwaddr[4], hwaddr[5]);
cvsdist 2b639f3
		}
cvsdist 2b639f3
	}
cvsdist 2b639f3
cvsdist 2b639f3
	if (wol_passwd_sz > 0) {
cvsdist 2b639f3
		memcpy(outpack+pktsize, wol_passwd, wol_passwd_sz);
cvsdist 2b639f3
		pktsize += wol_passwd_sz;
cvsdist 2b639f3
	}
cvsdist 2b639f3
cvsdist 2b639f3
	if (verbose > 1) {
cvsdist 2b639f3
		printf("The final packet is: ");
cvsdist 2b639f3
		for (i = 0; i < pktsize; i++)
cvsdist 2b639f3
			printf(" %2.2x", outpack[i]);
cvsdist 2b639f3
		printf(".\n");
cvsdist 2b639f3
	}
cvsdist 2b639f3
cvsdist 2b639f3
	/* This is necessary for broadcasts to work */
cvsdist 2b639f3
	if (setsockopt(s, SOL_SOCKET, SO_BROADCAST, (char *)&one, sizeof(one)) < 0)
cvsdist 2b639f3
		perror("setsockopt: SO_BROADCAST");
cvsdist 2b639f3
ebeb31b
#if defined(PF_PACKET)
ebeb31b
	{
ebeb31b
		struct ifreq ifr;
Jaromír Končický 0373077
		strncpy(ifr.ifr_name, ifname, sizeof(ifr.ifr_name));
ebeb31b
		if (ioctl(s, SIOCGIFINDEX, &ifr) == -1) {
ebeb31b
			fprintf(stderr, "SIOCGIFINDEX on %s failed: %s\n", ifname,
ebeb31b
					strerror(errno));
ebeb31b
			return 1;
ebeb31b
		}
ebeb31b
		memset(&whereto, 0, sizeof(whereto));
ebeb31b
		whereto.sll_family = AF_PACKET;
ebeb31b
		whereto.sll_ifindex = ifr.ifr_ifindex;
ebeb31b
		/* The manual page incorrectly claims the address must be filled.
ebeb31b
		   We do so because the code may change to match the docs. */
ebeb31b
		whereto.sll_halen = ETH_ALEN;
ebeb31b
		memcpy(whereto.sll_addr, outpack, ETH_ALEN);
ebeb31b
ebeb31b
	}
ebeb31b
#else
cvsdist 2b639f3
	whereto.sa_family = 0;
cvsdist 2b639f3
	strcpy(whereto.sa_data, ifname);
ebeb31b
#endif
cvsdist 2b639f3
ebeb31b
	if ((i = sendto(s, outpack, pktsize, 0, (struct sockaddr *)&whereto,
ebeb31b
					sizeof(whereto))) < 0)
cvsdist 2b639f3
		perror("sendto");
cvsdist 2b639f3
	else if (debug)
cvsdist 2b639f3
		printf("Sendto worked ! %d.\n", i);
cvsdist 2b639f3
cvsdist 2b639f3
#ifdef USE_SEND
7ab46c3
	if (bind(s, (struct sockaddr *)&whereto, sizeof(whereto)) < 0)
cvsdist 2b639f3
		perror("bind");
cvsdist 2b639f3
	else if (send(s, outpack, 100, 0) < 0)
cvsdist 2b639f3
		perror("send");
cvsdist 2b639f3
#endif
cvsdist 2b639f3
#ifdef USE_SENDMSG
cvsdist 2b639f3
	{
7ab46c3
		struct msghdr msghdr = { 0,};
cvsdist 2b639f3
		struct iovec iovector[1];
cvsdist 2b639f3
		msghdr.msg_name = &whereto;
cvsdist 2b639f3
		msghdr.msg_namelen = sizeof(whereto);
cvsdist 2b639f3
		msghdr.msg_iov = iovector;
cvsdist 2b639f3
		msghdr.msg_iovlen = 1;
cvsdist 2b639f3
		iovector[0].iov_base = outpack;
cvsdist 2b639f3
		iovector[0].iov_len = pktsize;
cvsdist 2b639f3
		if ((i = sendmsg(s, &msghdr, 0)) < 0)
cvsdist 2b639f3
			perror("sendmsg");
cvsdist 2b639f3
		else if (debug)
cvsdist 2b639f3
			printf("sendmsg worked, %d (%d).\n", i, errno);
cvsdist 2b639f3
	}
cvsdist 2b639f3
#endif
cvsdist 2b639f3
cvsdist 2b639f3
	return 0;
cvsdist 2b639f3
}
cvsdist 2b639f3
ebeb31b
/* Convert the host ID string to a MAC address.
ebeb31b
   The string may be a
ebeb31b
	Host name
ebeb31b
    IP address string
ebeb31b
	MAC address string
ebeb31b
*/
cvsdist 2b639f3
ebeb31b
static int get_dest_addr(const char *hostid, struct ether_addr *eaddr)
ebeb31b
{
ebeb31b
	struct ether_addr *eap;
ebeb31b
ebeb31b
	eap = ether_aton(hostid);
ebeb31b
	if (eap) {
ebeb31b
		*eaddr = *eap;
ebeb31b
		if (debug)
ebeb31b
			fprintf(stderr, "The target station address is %s.\n",
ebeb31b
					ether_ntoa(eaddr));
ebeb31b
	} else if (ether_hostton(hostid, eaddr) == 0) {
ebeb31b
		if (debug)
ebeb31b
			fprintf(stderr, "Station address for hostname %s is %s.\n",
ebeb31b
					hostid, ether_ntoa(eaddr));
ebeb31b
	} else {
cvsdist 2b639f3
		(void)fprintf(stderr,
ebeb31b
					  "ether-wake: The Magic Packet host address must be "
ebeb31b
					  "specified as\n"
ebeb31b
					  "  - a station address, 00:11:22:33:44:55, or\n"
ebeb31b
					  "  - a hostname with a known 'ethers' entry.\n");
ebeb31b
		return -1;
cvsdist 2b639f3
	}
ebeb31b
	return 0;
ebeb31b
}
ebeb31b
ebeb31b
ebeb31b
static int get_fill(unsigned char *pkt, struct ether_addr *eaddr)
ebeb31b
{
ebeb31b
	int offset, i;
ebeb31b
	unsigned char *station_addr = eaddr->ether_addr_octet;
cvsdist 2b639f3
cvsdist 2b639f3
	if (opt_broadcast)
cvsdist 2b639f3
		memset(pkt+0, 0xff, 6);
cvsdist 2b639f3
	else
cvsdist 2b639f3
		memcpy(pkt, station_addr, 6);
cvsdist 2b639f3
	memcpy(pkt+6, station_addr, 6);
cvsdist 2b639f3
	pkt[12] = 0x08;				/* Or 0x0806 for ARP, 0x8035 for RARP */
cvsdist 2b639f3
	pkt[13] = 0x42;
cvsdist 2b639f3
	offset = 14;
cvsdist 2b639f3
cvsdist 2b639f3
	memset(pkt+offset, 0xff, 6);
cvsdist 2b639f3
	offset += 6;
cvsdist 2b639f3
cvsdist 2b639f3
	for (i = 0; i < 16; i++) {
cvsdist 2b639f3
		memcpy(pkt+offset, station_addr, 6);
cvsdist 2b639f3
		offset += 6;
cvsdist 2b639f3
	}
cvsdist 2b639f3
	if (debug) {
cvsdist 2b639f3
		fprintf(stderr, "Packet is ");
cvsdist 2b639f3
		for (i = 0; i < offset; i++)
cvsdist 2b639f3
			fprintf(stderr, " %2.2x", pkt[i]);
cvsdist 2b639f3
		fprintf(stderr, ".\n");
cvsdist 2b639f3
	}
cvsdist 2b639f3
	return offset;
cvsdist 2b639f3
}
cvsdist 2b639f3
cvsdist 2b639f3
static int get_wol_pw(const char *optarg)
cvsdist 2b639f3
{
cvsdist 2b639f3
	int passwd[6];
cvsdist 2b639f3
	int byte_cnt;
cvsdist 2b639f3
	int i;
cvsdist 2b639f3
cvsdist 2b639f3
	byte_cnt = sscanf(optarg, "%2x:%2x:%2x:%2x:%2x:%2x",
cvsdist 2b639f3
					  &passwd[0], &passwd[1], &passwd[2],
cvsdist 2b639f3
					  &passwd[3], &passwd[4], &passwd[5]);
cvsdist 2b639f3
	if (byte_cnt < 4)
cvsdist 2b639f3
		byte_cnt = sscanf(optarg, "%d.%d.%d.%d",
cvsdist 2b639f3
						  &passwd[0], &passwd[1], &passwd[2], &passwd[3]);
cvsdist 2b639f3
	if (byte_cnt < 4) {
cvsdist 2b639f3
		fprintf(stderr, "Unable to read the Wake-On-LAN password.\n");
cvsdist 2b639f3
		return 0;
cvsdist 2b639f3
	}
cvsdist 2b639f3
	printf(" The Magic packet password is %2.2x %2.2x %2.2x %2.2x (%d).\n",
cvsdist 2b639f3
		   passwd[0], passwd[1], passwd[2], passwd[3], byte_cnt);
cvsdist 2b639f3
	for (i = 0; i < byte_cnt; i++)
cvsdist 2b639f3
		wol_passwd[i] = passwd[i];
cvsdist 2b639f3
	return wol_passwd_sz = byte_cnt;
cvsdist 2b639f3
}
cvsdist 2b639f3
cvsdist 2b639f3
#if 0
cvsdist 2b639f3
{
cvsdist 2b639f3
	to = (struct sockaddr_in *)&whereto;
cvsdist 2b639f3
	to->sin_family = AF_INET;
cvsdist 2b639f3
	if (inet_aton(target, &to->sin_addr)) {
cvsdist 2b639f3
		hostname = target;
cvsdist 2b639f3
	}
cvsdist 2b639f3
	memset (&sa, 0, sizeof sa);
cvsdist 2b639f3
	sa.sa_family = AF_INET;
cvsdist 2b639f3
	strncpy (sa.sa_data, interface, sizeof sa.sa_data);
cvsdist 2b639f3
	sendto (sock, buf, bufix + len, 0, &sa, sizeof sa);
cvsdist 2b639f3
	strncpy (sa.sa_data, interface, sizeof sa.sa_data);
cvsdist 2b639f3
#if 1
cvsdist 2b639f3
	sendto (sock, buf, bufix + len, 0, &sa, sizeof sa);
cvsdist 2b639f3
#else
cvsdist 2b639f3
	bind (sock, &sa, sizeof sa);
cvsdist 2b639f3
	connect();
cvsdist 2b639f3
	send (sock, buf, bufix + len, 0);
cvsdist 2b639f3
#endif
cvsdist 2b639f3
}
cvsdist 2b639f3
#endif
cvsdist 2b639f3
cvsdist 2b639f3

cvsdist 2b639f3
/*
cvsdist 2b639f3
 * Local variables:
cvsdist 2b639f3
 *  compile-command: "gcc -O -Wall -o ether-wake ether-wake.c"
cvsdist 2b639f3
 *  c-indent-level: 4
cvsdist 2b639f3
 *  c-basic-offset: 4
cvsdist 2b639f3
 *  c-indent-level: 4
cvsdist 2b639f3
 *  tab-width: 4
cvsdist 2b639f3
 * End:
cvsdist 2b639f3
 */