Wake-on-LAN

何も設定しなくても Ricoh IT2 は起きた。
とりあえず Wake up On Lan Tool を使った。

LAN で起動するまで

要は NIC に特殊な UDP パケットが届けば起動する。

  1. マジックパケットをブロードキャストする
  2. NICマジックパケットを検知する
    • UDP/7 か UDP/9 が正式らしいが、事実上 UDP であればポートは何でもいい
    • データは ff ff ff ff ff ff に送り先の NICMAC x 16
    • セキュリティは無い
  3. 起動する

WAN で起動するまで

別に難しくない。ルーターの NAT でブロードキャストアドレスを指定できるかだけが問題。

  1. ルーターに NAT エントリを追加する
    • プロトコル: UDP
    • 変換元ポート: 適当
    • 変換後アドレス: 192.168.0.255 とかのブロードキャストアドレス
    • 変換後ポート: 適当
  2. ホスト名の (IP なり DDNS なり) 適当に指定したポートにマジックパケットを送る
  3. ルーターが NAT でマジックパケットをブロードキャストする
  4. 後は LAN 内と同等

magicpacket.py

というわけでさくさくっと実装。バイナリ
LAN: magicpacket.py XX:XX:XX:XX:XX:XX
WAN: magicpacket.py -d example.com:port XX:XX:XX:XX:XX:XX

"""magicpacket.py"""
import re, socket
from optparse import OptionParser


if __name__ == '__main__':
    parser = OptionParser(usage='Usage: %prog [options] MAC-address')
    parser.add_option('-d', '--dest', '--destination',
        metavar='hostname[:port]',
        help='destination address (default=%default)',
        dest='dest', default='255.255.255.255:7')

    options, args = parser.parse_args()
    if not args:
        parser.error('no MAC address given')

    if ':' in options.dest:
        addr = options.dest.split(':', 1)
        addr[1] = int(addr[1])
    else:
        addr = options.dest, 7

    if addr[0] == '255.255.255.255':
        addr_list = []
        for ifaddr in socket.gethostbyname_ex(socket.gethostname())[2]:
            addr_list.append((ifaddr.rsplit('.', 1)[0] + '.255', addr[1]))
    else:
        addr_list = [addr, ]

    for arg in args:
        arg = re.sub('[^0-9A-Fa-f]', '', arg)
        mac = ''.join(chr(int(i+j, 16)) for i, j in zip(arg[::2], arg[1::2]))

        for addr in addr_list:
            sock = socket.socket(
                socket.AF_INET, socket.SOCK_DGRAM, socket.IPPROTO_UDP)
            sock.sendto('\xff' * 6 + mac * 16, tuple(addr))
            #sock.sendto('\xff' * 6 + mac * 20, tuple(addr))