Blame SOURCES/ether-wake.c

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