/* * Quick copy paste combo of * wolc: https://github.com/timofurrer/WOL * ether-wake.c: v1.09 11/12/2003 Donald Becker, http://www.scyld.com/ * so we can have one tool that supports RAW mode (ethernet frame type 0x0842) * and encapsulated UDP mode (like the wol or wakeonlan cmdline utils do). * * GPL2 */ static const char usage_msg[] = "Usage:\n" " %s [-b] [-i ] [-p aa:bb:cc:dd[:ee:ff]] \\\n" " 00:11:22:33:44:55 [, 11:22:33:44:55:66 [, ...]]\n" " %s -d [-p aa:bb:cc:dd[:ee:ff]] \\\n" " 00:11:22:33:44:55 [, 11:22:33:44:55:66 [, ...]]\n" "\n" "The first variant sends a raw ethernet frame with type 0x0842 (WoL) to the\n" "specified destination(s). In this mode, the source interface must be known\n" "(-i), which will default to eth0 if omitted.\n" "\n" "The second variant sends the UDP version of a WoL packet to port 9 of the\n" "given destination(s). This required the destination parameter (-d) to be set\n" "to either the limited broadcast address, or the directed broadcast address of\n" "the destination network, i.e. 10.1.2.255 if the destination host is\n" "10.1.2.3/24.\n" "This requires the final router connected to the destination subnet to be\n" "configured accordingly.\n" "\n" "Options for raw mode (etherwake):\n" " --broadcast\n" " -b Send wake-up packet to the broadcast MAC-address, instead of\n" " destination MAC address.\n" " --interface IFNAME\n" " -i IFNAME Use interface IFNAME instead of the default 'eth0'.\n" "\n" "Options for UDP mode (wakeonlan):\n" " --destination IPADDR\n" " -d IPADDR Destination (broadcast) IP address.\n" "\n" "Common options:\n" " --verbose\n" " -v Verbose mode, print some additional information.\n" " --debug\n" " -D Increase the debug level, print much more information.\n" " --password PASSWD\n" " -p PASSWD Append the four or six byte password PW to the packet.\n" " A password is only required for a few adapter types.\n" " The password may be specified in ethernet hex format\n" " or dotted decimal (Internet address),\n" " f.i. 00:22:44:66:88:aa or 192.168.1.1\n" "\n" "UDP mode will be selected if -d is passed on the command line. Any raw mode\n" "options (-b, -i) won't work in this mode so program execution is aborted.\n" ; #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #define PACKET_SIZE (200) #define CHECK_PACKET_SIZE(x) do { \ if ( (x) >= PACKET_SIZE ) { fprintf( stderr, "Packet size overflow.\n" ); abort(); } \ } while(0) static struct { const char *ifname; struct in_addr dest; char wolpw[6]; int wolpwlen; bool broadcast; bool verbose; // Spam bool debug; // Even more spam } options; static int parse_errors = 0; static int get_dest_addr(const char *arg, struct ether_addr *dest_ether); static int get_fill(unsigned char *pkt, const uint8_t *dest_ether, bool with_header); static void get_wol_pw(const char *optarg); static int get_source_addr(int s, const char *ifname, unsigned char *output); static struct in_addr get_ip(const char *arg); static int rawsocket(); static int udpsocket(); static int etherwake(int count, char **macs); static int wakeonlan(int count, char **maclist); static void print_usage(const char *a0) { printf( usage_msg, a0, a0 ); } int main(int argc, char **argv) { int opt; bool gotip = false, gotif = false; static const char *optString = "bDi:p:vhd:"; static const struct option longOpts[] = { { "broadcast", no_argument, NULL, 'b' }, { "debug", no_argument, NULL, 'D' }, { "interface", required_argument, NULL, 'i' }, { "password", required_argument, NULL, 'p' }, { "verbose", no_argument, NULL, 'v' }, { "help", no_argument, NULL, 'h' }, { "destination", required_argument, NULL, 'd' }, { 0, 0, 0, 0 } }; // Conveinience: Support MAC addr with dashes for ( int i = 1; i < argc; ++i ) { int j = 0; while ( argv[i][j] != '\0' ) { if ( argv[i][j] == '-' && j > 1 ) argv[i][j] = ':'; j++; } } while ( ( opt = getopt_long( argc, argv, optString, longOpts, NULL ) ) != -1 ) { switch ( opt ) { case 'b': options.broadcast = true; gotif = true; break; case 'D': options.debug = true; break; case 'i': options.ifname = optarg; gotif = true; break; case 'p': get_wol_pw( optarg ); break; case 'v': options.verbose = true; break; case 'h': print_usage( argv[0] ); return 0; case 'd': options.dest = get_ip( optarg ); gotip = true; break; case '?': parse_errors++; break; } } if ( gotip && gotif ) { fprintf( stderr, "Cannot use -d and -b/-i at the same time.\n" ); parse_errors++; } if ( parse_errors == 0 && optind >= argc ) { fprintf( stderr, "No MAC address(es) given.\n" ); parse_errors++; } if ( parse_errors != 0 ) { fprintf( stderr, "Try %s --help.\n", argv[0] ); } // Seems we're somewhat prepared to go if ( !gotip ) { // raw (etherwake) mode if ( options.verbose ) { printf( "Using RAW mode.\n" ); } return etherwake( argc - optind, argv + optind ); } // UDP (wakeonlan) mode if ( options.verbose ) { printf( "Using UDP mode.\n" ); } return wakeonlan( argc - optind, argv + optind ); } static int etherwake(int count, char **maclist) { unsigned char outpack[PACKET_SIZE]; int s = rawsocket(); if ( s == -1 ) return 2; if ( options.ifname == NULL ) { options.ifname = "eth0"; } #if defined(PF_PACKET) struct sockaddr_ll whereto = {0}; do { struct ifreq ifr; snprintf( ifr.ifr_name, IFNAMSIZ, "%s", options.ifname ); if (ioctl(s, SIOCGIFINDEX, &ifr) == -1) { perror( "ioctl SIOCGIFINDEX on raw socket" ); fprintf( stderr, "Are you sure %s exists?\n", options.ifname ); return 3; } whereto.sll_family = AF_PACKET; whereto.sll_ifindex = ifr.ifr_ifindex; } while (0); #else struct sockaddr whereto = {0}; /* who to wake up */ #endif if ( get_source_addr( s, options.ifname, outpack + 6 ) != 0 ) { fprintf( stderr, "Cannot determine source MAC address for outgoing packet, using bogus one\n" ); outpack[6] = 2; outpack[7] = outpack[8] = 34; outpack[9] = outpack[10] = outpack[11] = 56; } struct ether_addr dest_ether; for ( int idx = 0; idx < count; ++idx ) { if ( get_dest_addr( maclist[idx], &dest_ether ) != 0 ) { fprintf( stderr, "Cannot parse mac address '%s', skipping...\n", maclist[idx] ); continue; } int packet_size = get_fill( outpack, dest_ether.ether_addr_octet, true ); if ( options.wolpwlen > 0 ) { memcpy( outpack + packet_size, options.wolpw, options.wolpwlen ); packet_size += options.wolpwlen; } CHECK_PACKET_SIZE( packet_size ); if ( options.debug ) { printf( "The final packet for %s is:", maclist[idx] ); for ( int i = 0; i < packet_size; i++ ) printf( " %2.2x", outpack[i] ); printf( ".\n" ); } #if defined(PF_PACKET) /* The manual page incorrectly claims the address must be filled. We do so because the code may change to match the docs. */ whereto.sll_halen = ETH_ALEN; memcpy(whereto.sll_addr, outpack, ETH_ALEN); #else whereto.sa_family = 0; snprintf( whereto.sa_data, sizeof(whereto.sa_data), "%s", options.ifname ); #endif printf( "Sending magic packet to %s...\n", maclist[idx] ); int bytes = sendto( s, outpack, packet_size, 0, (struct sockaddr *)&whereto, sizeof(whereto) ); if ( bytes == -1 ) { perror( "sendto" ); } else if ( bytes != packet_size ) { fprintf( stderr, "Packet truncated, sent %d of %d bytes!\n", bytes, packet_size ); } else if ( options.debug ) { printf( "Sendto worked! %d bytes.\n", bytes ); } } close( s ); return 0; } static int rawsocket() { int s; int one = 1; /* Note: PF_INET, SOCK_DGRAM, IPPROTO_UDP would allow SIOCGIFHWADDR to work as non-root, but we need SOCK_PACKET to specify the Ethernet destination address. */ #if defined(PF_PACKET) s = socket(PF_PACKET, SOCK_RAW, 0); #else s = socket(AF_INET, SOCK_PACKET, SOCK_PACKET); #endif if (s == -1) { if (errno == EPERM) { fprintf(stderr, "This program must be run as root for raw mode to work.\n"); } else { perror("socket"); } return -1; } /* This is necessary for broadcasts to work */ if ( setsockopt( s, SOL_SOCKET, SO_BROADCAST, (char *)&one, sizeof(one) ) == -1 ) { perror( "setsockopt: SO_BROADCAST" ); return -1; } return s; } int udpsocket() { int sock; int optval = 1; sock = socket( AF_INET, SOCK_DGRAM, IPPROTO_UDP ); if ( sock == -1 ) { perror( "socket" ); return -1; } if ( setsockopt( sock, SOL_SOCKET, SO_BROADCAST, (char *) &optval, sizeof( optval ) ) == -1 ) { perror( "setsockopt: SO_BROADCAST" ); return -1; } return sock; } static int wakeonlan(int count, char **maclist) { struct ether_addr dest_ether; unsigned char outpack[PACKET_SIZE]; int sock = udpsocket(); if ( sock == -1 ) return 2; struct sockaddr_in addr = { .sin_family = AF_INET, .sin_port = htons( 9 ), .sin_addr = options.dest, }; for ( int idx = 0; idx < count; ++idx ) { if ( get_dest_addr( maclist[idx], &dest_ether ) != 0 ) { fprintf( stderr, "Cannot parse mac address '%s', skipping...\n", maclist[idx] ); continue; } int packet_size = get_fill( outpack, dest_ether.ether_addr_octet, false ); if ( options.wolpwlen > 0 ) { memcpy( outpack + packet_size, options.wolpw, options.wolpwlen ); packet_size += options.wolpwlen; } CHECK_PACKET_SIZE( packet_size ); if ( options.debug ) { printf( "The final packet for %s is:", maclist[idx] ); for ( int i = 0; i < packet_size; i++ ) printf( " %2.2x", outpack[i] ); printf( ".\n" ); } printf( "Sending magic packet to %s...\n", maclist[idx] ); int bytes = sendto( sock, outpack, packet_size, 0, (struct sockaddr *)&addr, sizeof(addr) ); if ( bytes == -1 ) { perror( "sendto" ); } else if ( bytes != packet_size ) { fprintf( stderr, "Packet truncated, sent %d of %d bytes!\n", bytes, packet_size ); } else if ( options.debug ) { printf( "Sendto worked! %d bytes.\n", bytes ); } } close( sock ); return 0; } static int get_fill(unsigned char *pkt, const uint8_t *bindestmac, bool with_header) { int offset = 0; int i; if ( with_header ) { if ( options.broadcast ) { memset( pkt, 0xff, 6 ); } else { memcpy( pkt, bindestmac, 6 ); } // Leave byte 6-11 alone, already set to source MAC addr // Next, type pkt[12] = 0x08; pkt[13] = 0x42; offset = 14; } // Write sync bytes payload memset(pkt+offset, 0xff, 6); // Repeat destination address 16 times offset += 6; for (i = 0; i < 16; i++) { memcpy(pkt + offset, bindestmac, 6); offset += 6; } CHECK_PACKET_SIZE( offset ); return offset; } static void get_wol_pw(const char *optarg) { unsigned int passwd[6]; int byte_cnt; int i; byte_cnt = sscanf(optarg, "%2x:%2x:%2x:%2x:%2x:%2x", &passwd[0], &passwd[1], &passwd[2], &passwd[3], &passwd[4], &passwd[5]); if (byte_cnt < 4) { // Support 4 chars in this notion byte_cnt = sscanf(optarg, "%u.%u.%u.%u", &passwd[0], &passwd[1], &passwd[2], &passwd[3]); } if (byte_cnt < 4) { fprintf(stderr, "Unable to read the Wake-On-LAN password.\n"); parse_errors++; return; } if (byte_cnt > 6) { fprintf(stderr, "The impossible happened.\n"); abort(); } printf(" The Magic packet password is %2.2x %2.2x %2.2x %2.2x (%d).\n", passwd[0], passwd[1], passwd[2], passwd[3], byte_cnt); options.wolpwlen = byte_cnt; for (i = 0; i < byte_cnt; i++) { options.wolpw[i] = passwd[i]; } } /** * Get MAC address of given interface (binary 6 byte) * returns 0 on success */ static int get_source_addr(int s, const char *ifname, unsigned char *output) { /* Fill in the source address, if possible. The code to retrieve the local station address is Linux specific. */ struct ifreq if_hwaddr; snprintf(if_hwaddr.ifr_name, IFNAMSIZ, "%s", ifname); if (ioctl(s, SIOCGIFHWADDR, &if_hwaddr) < 0) { fprintf(stderr, "SIOCGIFHWADDR on %s failed: %s\n", ifname, strerror(errno)); /* Magic packets still work if our source address is bogus, but we fail just to be anal. */ return 1; } memcpy(output, if_hwaddr.ifr_hwaddr.sa_data, 6); if ( options.verbose ) { unsigned char *hwaddr = (unsigned char*)if_hwaddr.ifr_hwaddr.sa_data; printf("The hardware address (SIOCGIFHWADDR) of %s is type %d " "%2.2x:%2.2x:%2.2x:%2.2x:%2.2x:%2.2x.\n", ifname, if_hwaddr.ifr_hwaddr.sa_family, hwaddr[0], hwaddr[1], hwaddr[2], hwaddr[3], hwaddr[4], hwaddr[5]); } return 0; } /** * Convert the host ID string to a MAC address. * The string may be a * Host name * IP address string * MAC address string */ static int get_dest_addr(const char *hostid, struct ether_addr *dest_ether) { struct ether_addr *eap; eap = ether_aton(hostid); if (eap != NULL) { *dest_ether = *eap; if (options.debug) { printf("The target station address is %s.\n", ether_ntoa(dest_ether)); } } else if (ether_hostton(hostid, dest_ether) == 0) { if (options.debug) { printf("Station address for hostname %s is %s.\n", hostid, ether_ntoa(dest_ether)); } } else { fprintf(stderr, "ether-wake: The Magic Packet host address must be " "specified as\n" " - a station address, 00:11:22:33:44:55, or\n" " - a station address, 00-11-22-33-44-55, or\n" " - a hostname with a known 'ethers' entry.\n"); return -1; } return 0; } static struct in_addr get_ip(const char *arg) { struct in_addr tmp; if ( inet_aton( arg, &tmp ) == 0 ) { fprintf( stderr, "Converting destination address via inet_aton failed.\n" ); parse_errors++; } return tmp; }