cvsdist 2b639f3
/* ether-wake.c: Send a magic packet to wake up sleeping machines. */
cvsdist 2b639f3
cvsdist 2b639f3
static char version_msg[] =
cvsdist 2b639f3
"ether-wake.c: v1.05 12/28/2000 Donald Becker, http://www.scyld.com/";
cvsdist 2b639f3
static char brief_usage_msg[] =
cvsdist 14587f6
"usage: ether-wake [-i <ifname>] [-p aa:bb:cc:dd[:ee:ff]] 00:11:22:33:44:55\n\
cvsdist 14587f6
   Use '-u' to see the complete set of options.\n";
cvsdist 2b639f3
static char usage_msg[] =
cvsdist 14587f6
"usage: ether-wake [-i <ifname>] [-p aa:bb:cc:dd[:ee:ff]] 00:11:22:33:44:55\n\
cvsdist 14587f6
\n\
cvsdist 14587f6
This program generates and transmits a Wake-On-LAN (WOL) \"Magic Packet\",\n\
cvsdist 14587f6
used for restarting machines that have been soft-powered-down\n\
cvsdist 14587f6
(ACPI D3-warm state).  It currently generates the standard AMD Magic Packet\n\
cvsdist 14587f6
format, with an optional password appended.\n\
cvsdist 14587f6
\n\
cvsdist 14587f6
The single required parameter is the Ethernet MAC (station) address\n\
cvsdist 14587f6
of the machine to wake.  This is typically retrieved with the 'arp'\n\
cvsdist 14587f6
program while the target machine is awake.\n\
cvsdist 14587f6
\n\
cvsdist 14587f6
Options:\n\
cvsdist 14587f6
	-b	Send wake-up packet to the broadcast address.\n\
cvsdist 14587f6
	-D	Increase the debug level.\n\
cvsdist 14587f6
	-i ifname	Use interface IFNAME instead of the default 'eth0'.\n\
cvsdist 14587f6
	-p <pw>		Append the four or six byte password PW to the packet.\n\
cvsdist 14587f6
			A password is only required for a few adapter types.\n\
cvsdist 14587f6
			The password may be specified in ethernet hex format\n\
cvsdist 14587f6
			or dotted decimal (Internet address)\n\
cvsdist 14587f6
		-p 00:22:44:66:88:aa\n\
cvsdist 14587f6
		-p 192.168.1.1\n\
cvsdist 2b639f3
";
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
cvsdist 2b639f3
	This source file is part of the network tricks package.
cvsdist 2b639f3
cvsdist 2b639f3
	The author may be reached as becker@scyld, or C/O
cvsdist 2b639f3
	 Scyld Computing Corporation
cvsdist 2b639f3
	 410 Severn Ave., Suite 210
cvsdist 2b639f3
	 Annapolis MD 21403
cvsdist 2b639f3
cvsdist 2b639f3
	The single required parameter is the Ethernet MAC (station) address
cvsdist 2b639f3
	of the machine to wake.  This is typically retrieved with the 'arp'
cvsdist 2b639f3
	program while the target machine is awake.
cvsdist 2b639f3
cvsdist 2b639f3
	Options:
cvsdist 2b639f3
		-b	Send wake-up packet to the broadcast address.
cvsdist 2b639f3
		-D	Increase the debug level.
cvsdist 2b639f3
		-i ifname	Use interface IFNAME instead of the default "eth0".
cvsdist 2b639f3
		-p <pw>		Append the four or six byte password PW to the packet.
cvsdist 2b639f3
					A password is only required for a few adapter types.
cvsdist 2b639f3
					The password may be specified in ethernet hex format
cvsdist 2b639f3
					or dotted decimal (Internet address)
cvsdist 2b639f3
		-p 00:22:44:66:88:aa
cvsdist 2b639f3
		-p 192.168.1.1
cvsdist 2b639f3
cvsdist 2b639f3
  Note: 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
cvsdist 2b639f3
*/
cvsdist 2b639f3

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
cvsdist 2b639f3
#ifdef UIO_MAXIOV
cvsdist 2b639f3
/*extern int setsockopt __P ((int __fd, int __level, int __optname,
cvsdist 2b639f3
  __ptr_t __optval, int __optlen));*/
cvsdist 2b639f3
#else				/* New, correct head files.  */
cvsdist 2b639f3
#include <sys/socket.h>
cvsdist 2b639f3
#endif
cvsdist 2b639f3
#ifdef USE_SENDMSG
cvsdist 2b639f3
#include <iovec.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
cvsdist 2b639f3
static int get_fill(unsigned char *pkt, char *arg);
cvsdist 2b639f3
static int get_wol_pw(const char *optarg);
cvsdist 2b639f3
cvsdist 2b639f3
int main(int argc, char *argv[])
cvsdist 2b639f3
{
cvsdist 2b639f3
	struct sockaddr whereto;	/* who to wake up */
cvsdist 2b639f3
	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;
cvsdist 2b639f3
	int i, c, pktsize;
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. */
cvsdist 2b639f3
	if ((s = socket(AF_INET, SOCK_PACKET, SOCK_PACKET)) < 0) {
cvsdist 2b639f3
		if (errno == EPERM)
cvsdist 2b639f3
			fprintf(stderr, "ether-wake must run as root\n");
cvsdist 2b639f3
		else
cvsdist 2b639f3
			perror("ether-wake: socket");
cvsdist 2b639f3
		if (! debug)
cvsdist 2b639f3
			return 2;
cvsdist 2b639f3
	}
cvsdist 2b639f3
	/* Don't revert if debugging allows a normal user to get the raw socket. */
cvsdist 2b639f3
	setuid(getuid());
cvsdist 2b639f3
cvsdist 2b639f3
	pktsize = get_fill(outpack, argv[optind]);
cvsdist 2b639f3
cvsdist 2b639f3
	/* Fill in the source address, if possible.
cvsdist 2b639f3
	   The code to retrieve the local station address is Linux specific. */
cvsdist 2b639f3
	if (! opt_no_src_addr){
cvsdist 2b639f3
		struct ifreq if_hwaddr;
cvsdist 2b639f3
		unsigned char *hwaddr = if_hwaddr.ifr_hwaddr.sa_data;
cvsdist 2b639f3
cvsdist 2b639f3
		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));
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
cvsdist 2b639f3
	whereto.sa_family = 0;
cvsdist 2b639f3
	strcpy(whereto.sa_data, ifname);
cvsdist 2b639f3
cvsdist 2b639f3
	if ((i = sendto(s, outpack, pktsize, 0, &whereto, 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
cvsdist 2b639f3
	if (bind(s, &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
	{
cvsdist 2b639f3
		struct msghdr msghdr;
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
cvsdist 2b639f3
static int get_fill(unsigned char *pkt, char *arg)
cvsdist 2b639f3
{
cvsdist 2b639f3
	int sa[6];
cvsdist 2b639f3
	unsigned char station_addr[6];
cvsdist 2b639f3
	int byte_cnt;
cvsdist 2b639f3
	int offset, i;
cvsdist 2b639f3
	char *cp;
cvsdist 2b639f3
cvsdist 2b639f3
	for (cp = arg; *cp; cp++)
cvsdist 2b639f3
		if (*cp != ':' && !isxdigit(*cp)) {
cvsdist 2b639f3
			(void)fprintf(stderr,
cvsdist 2b639f3
						  "ping: patterns must be specified as hex digits.\n");
cvsdist 2b639f3
			exit(2);
cvsdist 2b639f3
		}
cvsdist 2b639f3
cvsdist 2b639f3
	byte_cnt = sscanf(arg, "%2x:%2x:%2x:%2x:%2x:%2x",
cvsdist 2b639f3
					  &sa[0], &sa[1], &sa[2], &sa[3], &sa[4], &sa[5]);
cvsdist 2b639f3
	for (i = 0; i < 6; i++)
cvsdist 2b639f3
		station_addr[i] = sa[i];
cvsdist 2b639f3
	if (debug)
cvsdist 2b639f3
		fprintf(stderr, "Command line stations address is "
cvsdist 2b639f3
				"%2.2x:%2.2x:%2.2x:%2.2x:%2.2x:%2.2x.\n",
cvsdist 2b639f3
				sa[0], sa[1], sa[2], sa[3], sa[4], sa[5]);
cvsdist 2b639f3
cvsdist 2b639f3
	if (byte_cnt != 6) {
cvsdist 2b639f3
		(void)fprintf(stderr,
cvsdist 2b639f3
					  "ping: The Magic Packet address must be specified as "
cvsdist 2b639f3
					  "00:11:22:33:44:55.\n");
cvsdist 2b639f3
		exit(2);
cvsdist 2b639f3
	}
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
 */