Blame SOURCES/ether-wake.c

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