Blame SOURCES/ether-wake.c

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