3

A while ago, I wrote [with some help from Google] a small WOL script to switch on the computers in my network. Here is the script:

exec /usr/bin/python -x "$0" "$@"
#
node_lst = [
        'srv1 0a:1b:8c:0d:2e:7f',
        'srv2 0A-0B-4C-8D-CE:3F',
]
#
import os,sys,string,commands
import struct, socket
import re,random

retval = 0

mac_addr = "mac_addr.txt"
X = '([a-zA-Z0-9]{2}[:|\-|.]?){5}[a-zA-Z0-9]{2}'
S = re.compile(r'\s+')

mmap = {}

## First argument 'None' in str.translate is new in 2.6. 
## Previously, it was a string of 256 characters 
if sys.version_info < (2, 6):
    f1_arg = ''.join(chr(i) for i in xrange(256))
else:
    f1_arg = None

## broadcast address
sysOS = "uname -s"
BSD = "ifconfig | grep -w broadcast | cut -d\  -f 6"
LNX = "ip -o addr show | grep -w inet | grep -e eth | cut -d\  -f 9"
#
if commands.getoutput(sysOS) == "Linux":
    bCast = commands.getoutput(LNX)
elif commands.getoutput(sysOS) == "Darwin":
    bCast = commands.getoutput(BSD)
else:
    print "System not supported!!"
    sys_exit()

def WakeOnLan(mac_address):

    ## Building the Wake-On-LAN "Magic Packet"...
    ## Pad the synchronization stream.
    data = ''.join(['FFFFFFFFFFFF', mac_address * 20])
    msg = ''

    ## Split up the hex values and pack.
    for i in range(0, len(data), 2):
        msg = ''.join([msg, struct.pack('B', int(data[i: i + 2], 16))])

    ## ...and send it to the broadcast address using UDP
    s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
    s.setsockopt(socket.SOL_SOCKET, socket.SO_BROADCAST, 1)
    s.sendto(msg, (bCast, 9))
    s.close()

def sys_exit():
    sys.stdout.flush()
    sys.exit(1)

## check if hostname is provided
if len(sys.argv) != 2:
    print "Usage: %s <hostname>" % sys.argv[0]
    sys_exit()

for i in node_lst:
    # strip off everything from first "#" [if] found
    i = i.split('#',1)[0]
    if not re.search(X, i):
        continue

    h = S.split(i,1)[0]                 ## host name
    m = S.split(i,1)[-1]                ## MAC address
    mmap[h] = m.strip('\t|" "')

for j, k in mmap.iteritems():
    if sys.argv[1] == j:
        if not re.search(X.replace('zA-Z','fA-F'), k):
            print "Invalid MAC address [",k,"]; nothing to do!!"
            sys_exit()
        else:
            WakeOnLan(k.translate(f1_arg,':.-'))
            print "WOL request has been sent to %s [%s]" % (j,k)
            break
else:
    print "Host [%s] doesn't exist!!" % sys.argv[1]
    sys_exit()

Which works just fine from inside my home network (or LAN). How can I change the script to make it work for outside of my LAN? Any idea or suggestions? Cheers!!

MacUsers
  • 2,091
  • 3
  • 35
  • 56

2 Answers2

1

Configure your router to forward packets on a selection of 10 non-sequential ports to a machine on your LAN.

Devise some scheme based on say GMT Time + a hash to generate the port trigger sequence.

Have a python program (use scappy) on your command box inside that network listen for a series of syn packets.

The listener code would be analogous to the following tcpdump syntax:

sudo tcpdump -ni eth0 'tcp[tcpflags] & (tcp-syn) !=0'

Where it captures just syn packets.

Your program just sits there, waiting for the right syn sequence. When it receives the sequence, it runs your WOL script.

Done.

If you don't want to open ports, your script could instead poll a remote website, waiting for changes. Or listen for email fetched via email.

Taking your idea further, you could do fancy stuff like turn on your lights or boot up the TV.

Bryan Hunt
  • 3,685
  • 2
  • 24
  • 36
1

This is not possible because WOL packets are broadcast packets (since you can't know who to send it too). Home routers and especially ISP/Network routers discard all broadcast packets because else everytime you run this one script all the computers on the entire internet would receive your package, which would cause quite some clutter.

What you of course can do is write a small application that is on a computer that is running inside the WAN in which you wish to turn on all computers, and then have that application send a WOL packet. However this would require a computer with internet access to be turned on at all times.

Roy T.
  • 9,429
  • 2
  • 48
  • 70
  • I found several posts in the Internet where says it's possible using the routers IP address and using NAT/port-forwarding to a non-existence LAN IP address. Cheers!! – MacUsers Jun 12 '11 at 20:08
  • If you do find out how, please let everyone here know :). – Roy T. Jun 12 '11 at 20:09
  • Here is one, just found (I should have searched and tried before posting): `http://www.schuetzler.net/blog/4/first-python-program-wake-on-wan` haven't tested yet. – MacUsers Jun 12 '11 at 20:50
  • Yes, it worked!! Even my script is also working. Just need to change the line `s.sendto(msg, (bCast, 9))` to `s.sendto(msg, (r_ip, 9))`, where `r_ip` is the WAN side IP address of the router and `9` is port number where it's being forwarded to. The only thing: It didn't work with NAT setting to a non-existence IP address, you need to use the actual [LAN-side] IP address of the machine in question. Hope it helps. Cheers!! – MacUsers Jun 12 '11 at 21:52
  • Hey MacUser, how can you know the actual LAN IP address of a machine that is turned off? Turned off machines only have a physical MAC Address, not a live ip-address. – Roy T. Jun 13 '11 at 07:27
  • @Roy T: The router should have a internal IP <-> MAC mapping for the node (well, you need to do static-DHCP). You send the request to your WAN side IP (along with the post number) and the router forward the request to the particular host. In this case, you should have individual WOL forward rule (port to IP mapping) for each of your node. This is what I understand. I'm doing some more tests and let you know my findings. Cheers!! – MacUsers Jun 13 '11 at 09:17