Blame SOURCES/ether-wake.c

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