Blame SOURCES/ether-wake.c

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