Blame SOURCES/ether-wake.c

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