Using a Raspberry Pi as a Wake on Lan Forwarder
Author: The Man
Well I don’t have a Raspberry Pi yet but for $35 it’s a bargain and could be used as a WoL forwarder for those of you who don’t have a router that allow packet forwarding for Subnet Directed Broadcasts. This is a first draft of an app you can run.
1. Fire up your Raspberry Pi and download the developer tools, I am assuming your using a Debian based version of Linux. Simply type apt-get install build-essential
2. Copy the code below and place into a file e.g. nano wolf.c then save
3. Compile with gcc -o wolf wolf.c then run with ./wolf
* don’t forget to change to the name of your network interface #define ETHNAME “en1″ usually eth0 on Linux but en0 on a mac.
That’s basically all there is to it. I am going to look at running as a service and report back later.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 | // // wolf.c // // Created by Brian on 12/05/2012. // Copyright (c) 2012 Depicus. All rights reserved. // #include <stdio.h> #include <sys/socket.h> #include <netinet/in.h> #include <stdio.h> #include <string.h> #include <sys/types.h> #include <arpa/inet.h> #include <netdb.h> #include <unistd.h> #include <stdlib.h> #include <errno.h> #include <net/if.h> #include <sys/ioctl.h> #include <sys/types.h> #define ETHNAME "eth0" #define BUFLEN 102 char myipaddress[16]; char str[1024]; enum { ADDRS_SIZE = 8 }; struct in_addr bcast; struct in_addr nmask; static char netcard[16]; char const *getadapteraddress(char adapter[5]) { int fd; struct ifreq ifr; u_char *addr; u_char *braddr; u_char *naddr; fd = socket (AF_INET, SOCK_DGRAM,0); if ((fd = socket(AF_INET,SOCK_DGRAM,0)) < 0) { fprintf(stderr,"Error: Unable to create socket\n"); perror("socket"); return "-1"; } memset (&ifr, 0, sizeof (struct ifreq)); strcpy (ifr.ifr_name, adapter); ifr.ifr_addr.sa_family = AF_INET; ioctl(fd, SIOCGIFADDR, &ifr); addr=(u_char*)&(((struct sockaddr_in * )&ifr.ifr_addr)->sin_addr); //printf("eth %s, addr %d.%d.%d.%d\n", ifr.ifr_name,addr[0],addr[1],addr[2],addr[3]); strcat(netcard,ifr.ifr_name); sprintf (myipaddress,"%d.%d.%d.%d",addr[0],addr[1],addr[2],addr[3]); memset (&ifr, 0, sizeof (struct ifreq)); strcpy (ifr.ifr_name, adapter); ifr.ifr_addr.sa_family = AF_INET; ioctl(fd, SIOCGIFBRDADDR, &ifr); braddr=(u_char*)&(((struct sockaddr_in * )&ifr.ifr_broadaddr)->sin_addr); //printf("eth broadcast %d.%d.%d.%d\n",braddr[0],braddr[1],braddr[2],braddr[3]); memcpy(&bcast, &(*(struct sockaddr_in *)&ifr.ifr_broadaddr).sin_addr, 4); memset (&ifr, 0, sizeof (struct ifreq)); strcpy (ifr.ifr_name, adapter); ifr.ifr_addr.sa_family = AF_INET; ioctl(fd, SIOCGIFNETMASK, &ifr); naddr=(u_char*)&(((struct sockaddr_in * )&ifr.ifr_broadaddr)->sin_addr); //printf("eth subnet %d.%d.%d.%d\n",braddr[0],braddr[1],braddr[2],braddr[3]); memcpy(&nmask, &(*(struct sockaddr_in *)&ifr.ifr_broadaddr).sin_addr, 4); close(fd); return myipaddress; } // Main section void sendWoLPacket(char mesg[101]) { int sendSocket; struct sockaddr_in wt; memset(&wt, 0, sizeof(wt)); wt.sin_family = AF_INET; wt.sin_port = htons(9); wt.sin_addr.s_addr = inet_addr(inet_ntoa(bcast)); sendSocket = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP); int udpflag = 1; int retval; retval = setsockopt(sendSocket, SOL_SOCKET, SO_BROADCAST, &udpflag, sizeof(udpflag)); if (retval < 0) { sprintf (str,"failed to setsockopt: %s",strerror(errno)); printf("%s\n",str); } int res; res = sendto(sendSocket,mesg,BUFLEN,0,(struct sockaddr *)&wt,sizeof(wt)); if (res < 0) { sprintf (str,"failed to send: %s",strerror(errno)); printf("%s\n", str); } else { printf("Resent WoL on port %i\n",9); } } int main(int argc, const char * argv[]) { printf("Starting WoLf:\n"); char const *cptr; cptr = getadapteraddress(ETHNAME); printf("%s: inet %s netmask %s broadcast %s \n",netcard,cptr,inet_ntoa(nmask),inet_ntoa(bcast)); int sendSocket,n; struct sockaddr_in servaddr,cliaddr; socklen_t len; char mesg[101]; //wol packet size is 102 so 0 to 101 //int bytes; sendSocket=socket(AF_INET,SOCK_DGRAM,0); bzero(&servaddr,sizeof(servaddr)); servaddr.sin_family = AF_INET; servaddr.sin_addr.s_addr=htonl(INADDR_ANY); servaddr.sin_port=htons(4343); bind(sendSocket,(struct sockaddr *)&servaddr,sizeof(servaddr)); for (;;) { len = sizeof(cliaddr); n = recvfrom(sendSocket,mesg,1000,0,(struct sockaddr *)&cliaddr,&len); sendto(sendSocket,mesg,n,0,(struct sockaddr *)&cliaddr,sizeof(cliaddr)); printf("-------------------------------------------------------\n"); mesg[n] = 0; printf("\nPacket Received with length %i\n", n); //mesg[n] = 0; if (!n == 102) continue; printf("mac address: %02X:%02X:%02X:%02X:%02X:%02X \n",(unsigned char)mesg[6],(unsigned char)mesg[7],(unsigned char)mesg[8],(unsigned char)mesg[9],(unsigned char)mesg[10],(unsigned char)mesg[11]); char ffAddress[18] = ""; char mAddress[18] = ""; // check that the first six digits are FF sprintf (ffAddress,"%02X:%02X:%02X:%02X:%02X:%02X",(unsigned char)mesg[0],(unsigned char)mesg[1],(unsigned char)mesg[2],(unsigned char)mesg[3],(unsigned char)mesg[4],(unsigned char)mesg[5]); if (!strncmp(ffAddress,"FF:FF:FF:FF:FF:FF",17) == 0) { continue; } // check we have six repeating mac addresses int y; for (y = 0; y < 6; y++) { int mply = 6 * (y + 1); sprintf (mAddress,"%02X:%02X:%02X:%02X:%02X:%02X",(unsigned char)mesg[mply],(unsigned char)mesg[mply+1],(unsigned char)mesg[mply+2],(unsigned char)mesg[mply+3],(unsigned char)mesg[mply+4],(unsigned char)mesg[mply+5]); printf("%i %s \n",y, mAddress); if (y > 0) { if (!strncmp(mAddress,ffAddress,strlen(ffAddress)) == 0) { continue; } } stpcpy(ffAddress,mAddress); } //int y; //for (y = 0; y < sizeof(mesg); y++) //{ // printf("%i = %02X ",y,(unsigned char)mesg[y]); //} printf("End Packet\n"); sendWoLPacket(mesg); } printf("Fin\n"); return 0; } |
Tags: Raspberry Pi







May 28th, 2012 at 1:39 pm
Your idea to use a small hardware device to circumvent the broadcast limitation is a good idea. I use Jump routers in virtual cable mode for this (see http://www.automonitor.net/jump), one on each side. With this particular VPN set up, you can broadcast and use any remote access without router hassle. Jump also works with a 3G modem inserted directly in the USB port of the small device, the mode can still be virtual cable. If needed in a LAN, you can set static ip adress. Set up takes only minutes and is very straight forward.
Í used your depicus ipad app over the Jump routers from one adsl network to another with good result.
So, one hardware device could help, but inexpensive hardware based vpn is better.
Kind regards
Jonas
May 28th, 2012 at 2:30 pm
Like that idea, might have to get one to have a play with :)
June 11th, 2012 at 12:37 pm
Jonas,
The Jump router in virtual cable mode sounds very interesting. Is it possible to use this Jump router behind my home router to wake up any home computer without using a 3G modem?
My Asus RT-N16 router has its own VPN server. I would like to use VPN client from remote pc to wake WHS server to do remote backups. I think the router broadcast limitation is stopping me.
Raspberry Pi and Jump router would be some fun home projects to try to resolve my WOL problem.
Thanks for sharing!!
Clark
July 5th, 2012 at 8:25 am
Great idea and it’s exactly what I was trying to get working with other methods but still struggled with.
I’ve tried the script on my Raspberry Pi, it compiles and launches OK but I’m stuck at that point.
Can somebody confirm what I need to do next to enable WOL over the internet by using my rpi as a forwarder please?
Thanks for the great idea!
Russ
July 5th, 2012 at 8:44 am
Hi Russ
Well got my Pi last week and got it up and running so thing to check.
1. Make sure you change “#define ETHNAME “en1″” to the name of the interface on the Pi, on mine it’s eth0 but ifconfig will tell you.
2. By default it listens on port 4343 and sends on port 9 but you can change if you want, just find those values in the code.
3. On your adsl box just do a port forward rule from the outside world to the ip of your Pi using port 4343 or whatever port you change it to.
That should be it. Any problems just ask.
July 5th, 2012 at 1:39 pm
This works great locally and in theory should work once I create a port forward in my router. The issue I do have is that I believe my ISP is blocking UDP packets for some reason. I have tried several routers and will be trying a basic Netgear router this afternoon maybe.
Would there be any other way to be able to get WOL broadcast packets inside a network by using a Pi as a forwarder when using a port forward rule on a ADSL router. Something along the lines of creating a listening service on the Pi that we send commands to over TCP instead of UDP? I thought about using something similar to Hamachi or VPN tunnels but that could get messy!
Russ
July 5th, 2012 at 2:16 pm
Hi Russ
You might be blocked on lower ports but never heard of them being blocked on high ports above 1024.
How are you sending from the web ?
Technically you could use TCP but that would need a tcp sender and receiver which I don’t think anybody does.
July 5th, 2012 at 2:50 pm
I tried port 4343 and 43434 with the same results as well as trying port 7.
I also tried using your woli.aspx as well as your GUI to send the WOL packets.
I’ve been able to launch the WOL monitor on a different server in a different office and use the same procedure as above and the monitor shows the packets working fine without any issues. It’s this that makes me think that I have an issue with my ISP blocking UDP altogether.
July 5th, 2012 at 3:53 pm
An idea…
I’ve installed Hamachi onto the Pi to get around my ISP potentially blocking UDP ports and then I have installed Hamachi onto a different computer. I’ve then run this forwarder code on the Pi specifying the Hamachi interface which works as much as it accepts the packet but I’m assuming it’s trying to then relay it back onto the same interface “ham0″ Is that correct?
Is it possible to add code to be able to specify what interface to use to send the magic packet back out onto? So in this case it would be “eth0″
Russ
July 5th, 2012 at 4:03 pm
Well this I found on MSDN
“For Sockets Using IP (Version 4)
To send a broadcast (on a SOCK_DGRAM only), the address pointed to by the to parameter can be constructed to contain the special IPv4 address INADDR_BROADCAST (defined in Winsock2.h), together with the intended port number. If the address pointed to by the to parameter contains the INADDR_BROADCAST address and intended port, then the broadcast will be sent out on all interfaces to that port.
If the broadcast should be sent out only on a specific interface, then the address pointed to by the to parameter should contain the subnet broadcast address for the interface and the intended port. For example, an IPv4 network address of 192.168.1.0 with a subnet mask of 255.255.255.0 would use a subnet broadcast address of 192.168.1.255.”
Not sure how you set up Hamachi, never heard of it before but going to have a play tonight :)
August 7th, 2012 at 10:36 am
Awesome, works like a charm. Hope you’re still working on making it into a service.
August 7th, 2012 at 10:54 am
Yes just got olympic fever at the moment :)
August 10th, 2012 at 4:50 pm
Great !
I had the same idea a few months ago, and started to write a little magic packet forwarder for my brand new arduino uno + ethernet shield board.
If you’re interested, I wrote a tutorial about this little program, you can read it on my website here :
http://www.finalclap.com/tuto/arduino-wake-on-lan-repeater-80/
It’s in french but you can easily translate it to english to understand what I say, with google translate, or simply grab the source code & upload it to your arduino.
It works really great, I use it everyday to remote boot my personal computer from work and… ;)
Also, I added an optional feature : ip change logger (you need a PHP web hosting to run your own logger script, called by the arduino).
Everything is explained in the tutorial.