jpopelka / rpms / net-tools

Forked from rpms/net-tools 3 years ago
Clone

Blame SOURCES/ether-wake.c

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