go实现网络唤醒远程开机(Wake on Lan)

180it 2022-03-07 AM 1356℃ 0条

go实现网络唤醒远程开机(Wake on Lan)

这是一个创建于 2020-03-08 18:23:58 的主题,其中的信息可能已经有所发展或是发生改变。
源码位置: https://github.com/zzustu/wol
编译后的文件位置: https://github.com/zzustu/wol/releases

初学go语言,想着做一个远程开机的小程序放在路由器上面,能给电脑远程开机的小程序, 刚开始在电脑上可以唤醒成功,但是放在路由器上怎么也唤醒不成功,也抓不到唤醒魔包, 研究了一下发现是因为路由器是多网卡,魔包并没有走内网网卡,现在可以通过指定网卡 发送唤醒魔包了,

命令:wol -nic br-lan -mac XX:XX:XX:XX:XX:XX , br-lan 为替换成你实际网卡的名称,一般单网卡设备可以忽略该参数

现在贴出来,如果代码有哪里不合适的地方也请各位大佬指点。

package main

import (
    "bytes"
    "encoding/hex"
    "errors"
    "flag"
    "fmt"
    "net"
    "strings"
)

var help = `
环境说明:
    1. 被唤醒主机要支持Wake on Lan功能且已开启该功能
    2. 被唤醒主机在关机时应该是有线连接
    3. 请确保该程序与被唤醒主机在同一局域网中
参数说明:
    -mac 被唤醒主机的MAC地址 (必须输入)
    -nic 指定网卡发送唤醒魔包
使用说明:
    wol -mac 11:22:33:44:55:66 -nic eno1
注意事项:
    1. 路由器设备或有些主机有多张网卡, 如果不指定网卡唤醒魔包可能到达不了被唤醒主机
    2. MAC地址的格式可以是以下几种形式
        11:22:33:44:55:66 或
        11-22-33-44-55-66 或
        11:22-33:44:55-66 或
        112233445566
源码地址:
    https://github.com/zzustu/wol
`

// 网络唤醒魔包技术白皮书地址: https://www.amd.com/system/files/TechDocs/20213.pdf
func main() {
    mac := flag.String("mac", "", help)
    nic := flag.String("nic", "", help)
    flag.Parse()
    if len(*mac) == 0 {
        fmt.Printf("%s\n", help)
        return
    }

    hw := strings.Replace(strings.Replace(*mac, ":", "", -1), "-", "", -1)
    if len(hw) != 12 {
        fmt.Printf("MAC: [%s] 输入不正确.\n", *mac)
        return
    }

    macHex, err := hex.DecodeString(hw)
    if err != nil {
        fmt.Printf("MAC: [%s] 输入不正确.\n", *mac)
        return
    }

    // 广播MAC地址 FF:FF:FF:FF:FF:FF
    var bcast = []byte{0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}
    var buff bytes.Buffer
    buff.Write(bcast)
    for i := 0; i < 16; i++ {
        buff.Write(macHex)
    }

    // 获得唤醒魔包
    mp := buff.Bytes()
    if len(mp) != 102 {
        fmt.Printf("MAC: [%s] 输入不正确.\n", *mac)
        return
    }

    sendMagicPacket(mp, *nic)
}

// 向指定网卡发送唤醒魔包
func sendMagicPacket(mp []byte, nic string) {
    sender := net.UDPAddr{}
    if len(nic) != 0 {
        ip, err := interfaceIPv4ByName(nic)
        if err != nil {
            fmt.Printf("网卡[%s]错误: %s", nic, err)
            return
        }

        sender.IP = ip
    }

    target := net.UDPAddr{
        IP: net.IPv4bcast,
    }
    conn, err := net.DialUDP("udp", &sender, &target)
    if err != nil {
        fmt.Printf("创建UDP错误:%v", err)
        return
    }
    defer func() {
        _ = conn.Close()
    }()

    _, err = conn.Write(mp)
    if err != nil {
        fmt.Printf("魔包发送失败[%s]", err)
    } else {
        fmt.Printf("魔包发送成功")
    }
}

// 通过网卡名称获取该网卡绑定的IPv4
func interfaceIPv4ByName(nic string) (net.IP, error) {
    inter, err := net.InterfaceByName(nic)
    if err != nil {
        return nil, err
    }

    // 检查网卡是否正在工作
    if (inter.Flags & net.FlagUp) == 0 {
        return nil, errors.New("网卡未工作")
    }

    addrs, err := inter.Addrs()
    if err != nil {
        return nil, err
    }

    for _, addr := range addrs {
        if ip, ok := addr.(*net.IPNet); ok {
            if ipv4 := ip.IP.To4(); ipv4 != nil {
                return ipv4, nil
            }
        }
    }

    return nil, errors.New("找不到网卡绑定的IP")
}
支付宝打赏支付宝打赏 微信打赏微信打赏

如果文章或资源对您有帮助,欢迎打赏作者。一路走来,感谢有您!

标签: none

go实现网络唤醒远程开机(Wake on Lan)