目录

Magic Packets远程开机的代码


目录

前阵子在少数派上写了一篇水文发表在这里把手机变为生产力工具,打造免费的华为云电脑 https://sspai.com/post/52303,老婆那时候赞助我买的华为大屏手机,我才想着怎么把手机弄成PC机一样能方便的工作。

其实对于我有DDNS域名,再加上可以连入内网VPN,路由出口就是公网IP,剩下唯一需要解决的问题是如何远程开机,所以我就再水一篇帖贴代码,好久没写文章了。

在支持WOL功能的网卡上都支持开机唤醒,一般比较古老的十多年的机器都支持这个,这个唤醒的网络包叫做Magic Packets,是AMD发起的非事实的业界标准。

具体到对应的协议包是对应UDP的9号端口,基本包是6个字节的0xFF,然后16次重复目标接口的MAC地址,总共是102字节

这个用go实现的代码如下

  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
package main

import (
	"bytes"
	"encoding/binary"
	"flag"
	"fmt"
	"net"
	"os"
	"regexp"
)

type MACAddress [6]byte

type MagicPacket struct {
	header  [6]byte
	payload [16]MACAddress
}

var (
	delims = ":-"
	reMAC  = regexp.MustCompile(`^([0-9a-fA-F]{2}[` + delims + `]){5}([0-9a-fA-F]{2})$`)

	macAddr        string
	bcastInterface string
)

func init() {
	flag.StringVar(&macAddr, "mac", "", "wake up by MAC address, a valid MAC is like: 01-02-03-04-05-06, 01:02:03:04:05:06")
	flag.StringVar(&bcastInterface, "interface", "", "outbound interface to broadcast using")
	flag.Usage = usage
}

func usage() {
	flag.PrintDefaults()
	os.Exit(2)
}

func NewMagicBuff(mac string) (*MagicPacket, error) {
	var packet MagicPacket
	var macAddr MACAddress

	if !reMAC.MatchString(mac) {
		return nil, fmt.Errorf("%s is not a IEEE 802 MAC-48 address", mac)
	}

	hwAddr, err := net.ParseMAC(mac)
	if err != nil {
		return nil, err
	}

	for idx := range macAddr {
		macAddr[idx] = hwAddr[idx]
	}

	for idx := range packet.header {
		packet.header[idx] = 0xFF
	}

	for idx := range packet.payload {
		packet.payload[idx] = macAddr
	}

	return &packet, nil
}

func main() {
	var localaddr *net.UDPAddr
	flag.Parse()

	if macAddr == "" {
		usage()
	}

	fmt.Printf("Mac is %s, interface is %s \n", macAddr, bcastInterface)
	ief, _ := net.InterfaceByName(bcastInterface)
	addrs, _ := ief.Addrs()
	for _, addr := range addrs {
		switch ip := addr.(type) {
		case *net.IPNet:
			if ip.IP.DefaultMask() != nil {
				fmt.Println(ip.IP)
				localaddr = &net.UDPAddr{
					IP: ip.IP,
				}
			}
		}
	}

	//	ipwake := "192.168.2.15"
	//	addrip := net.ParseIP(ipwake)

	remoteAddr, _ := net.ResolveUDPAddr("udp", "255.255.255.255:9")

	conn, _ := net.DialUDP("udp", localaddr, remoteAddr)

	defer conn.Close()
	var mp *MagicPacket

	mp, err := NewMagicBuff(macAddr)
	if err != nil {
		return
	}

	var buf bytes.Buffer
	if err = binary.Write(&buf, binary.BigEndian, mp); err != nil {
		return
	}

	fmt.Printf("Attempting to send a magic packet to MAC %s, local IP: %s \n", macAddr, localaddr.IP)
	conn.Write(buf.Bytes())

	fmt.Println("Done!")
}

代码简单清晰就没写什么注释了,这段代码可以直接编译放到路由上运行,调用的时候一个参数是WAN口名称,写一段这个脚本就可以轻松调用了

1
2
3
4
5
6
#!/bin/sh
MAC_ADDR="01-02-03-04-05-06"
WAKEUP_BIN="wakeup"
LOCALIP_ADDR=$(ifconfig | awk 'NR==1 {if (index($1,":") == 0) print $1; else print substr($1, 0, index($1,":")-1)}')
Basepath=$(cd `dirname $0`; pwd)
$Basepath"/"$WAKEUP_BIN -mac "$MAC_ADDR" -interface "$LOCALIP_ADDR"