jpopelka / rpms / net-tools

Forked from rpms/net-tools 4 years ago
Clone

Blame SOURCES/ether-wake.c

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