【问题标题】:How to get all addresses and masks from local interfaces in go?如何从本地接口获取所有地址和掩码?
【发布时间】:2014-06-25 03:08:09
【问题描述】:

如何从 golang 的本地接口获取所有地址和掩码?

我需要与每个 IP 地址一起配置的实际网络掩码。

此代码在 Windows 7 中不显示网络掩码:

package main

import (
    "fmt"
    "log"
    "net"
)

func localAddresses() {
    ifaces, err := net.Interfaces()
    if err != nil {
        log.Print(fmt.Errorf("localAddresses: %v\n", err.Error()))
        return
    }
    for _, i := range ifaces {
        addrs, err := i.Addrs()
        if err != nil {
            log.Print(fmt.Errorf("localAddresses: %v\n", err.Error()))
            continue
        }
        for _, a := range addrs {
            log.Printf("%v %v\n", i.Name, a)
        }
    }
}

func main() {
    localAddresses()
}

更新:此问题已在 Go 中修复:https://github.com/golang/go/issues/5395

【问题讨论】:

    标签: networking go ip ip-address


    【解决方案1】:

    net.Interface 可能有多种类型的地址。 Addr 只是一个可能包含net.IPAddr 的接口。但是通过类型断言或类型切换,您可以访问实际的地址类型:

    package main
    
    import (
        "fmt"
        "net"
    )
    
    func localAddresses() {
        ifaces, err := net.Interfaces()
        if err != nil {
            fmt.Print(fmt.Errorf("localAddresses: %+v\n", err.Error()))
            return
        }
        for _, i := range ifaces {
            addrs, err := i.Addrs()
            if err != nil {
                fmt.Print(fmt.Errorf("localAddresses: %+v\n", err.Error()))
                continue
            }
            for _, a := range addrs {
                switch v := a.(type) {
                case *net.IPAddr:
                    fmt.Printf("%v : %s (%s)\n", i.Name, v, v.IP.DefaultMask())
                }
    
            }
        }
    }
    
    func main() {
        localAddresses()
    }
    

    编辑

    不幸的是,net 包没有返回地址的掩码。因此,您将不得不执行 net 包所做的低级系统调用。下面的代码是一个例子,但仍然需要解析ip和掩码:

    package main
    
    import (
        "fmt"
        "net"
        "os"
        "syscall"
        "unsafe"
    )
    
    func getAdapterList() (*syscall.IpAdapterInfo, error) {
        b := make([]byte, 1000)
        l := uint32(len(b))
        a := (*syscall.IpAdapterInfo)(unsafe.Pointer(&b[0]))
        // TODO(mikio): GetAdaptersInfo returns IP_ADAPTER_INFO that
        // contains IPv4 address list only. We should use another API
        // for fetching IPv6 stuff from the kernel.
        err := syscall.GetAdaptersInfo(a, &l)
        if err == syscall.ERROR_BUFFER_OVERFLOW {
            b = make([]byte, l)
            a = (*syscall.IpAdapterInfo)(unsafe.Pointer(&b[0]))
            err = syscall.GetAdaptersInfo(a, &l)
        }
        if err != nil {
            return nil, os.NewSyscallError("GetAdaptersInfo", err)
        }
        return a, nil
    }
    
    func localAddresses() error {
        ifaces, err := net.Interfaces()
        if err != nil {
            return err
        }
    
        aList, err := getAdapterList()
        if err != nil {
            return err
        }
    
        for _, ifi := range ifaces {
            for ai := aList; ai != nil; ai = ai.Next {
                index := ai.Index
    
                if ifi.Index == int(index) {
                    ipl := &ai.IpAddressList
                    for ; ipl != nil; ipl = ipl.Next {
    
                        fmt.Printf("%s: %s (%s)\n", ifi.Name, ipl.IpAddress, ipl.IpMask)
                    }
                }
            }
        }
        return err
    }
    
    func main() {
        err := localAddresses()
        if err != nil {
            panic(err)
        }
    }
    

    从 interface_windows.go 中无耻地借用了一些代码。甚至 cmets 也完好无损

    【讨论】:

    • 我需要实际的掩码,而不是有类的默认值。我看到 net.IPNet 类型提供了 net.IPNet.Mask。有没有办法将地址读取为 net.IPNet?
    • 查看net 包的文档/源代码,我看到他们只提供IP。因此,您需要做的是改用 syscall 包。我会更新我的答案。
    • 不错!但是它只显示一个接口的地址/掩码。固定在这里:play.golang.org/p/kJ0P7HnvDE 请更新答案然后我接受它!谢谢。
    • @Everton 啊,是的。我看到了我的错误。我已经用你的错误修复更新了代码。欢迎:)
    • syscall.IpAdapterInfo 在 Go 1.5 上有一些问题,它将在 Go 1.6 中修复。参考github.com/golang/go/issues/12551
    【解决方案2】:

    我正在修改@ANisus 答案并获取所有接口和掩码(在 Windows 10 和 WSL 上测试(Microsoft Ubuntu 16.04.5 LTS):

    package main
    
    import (
        "fmt"
        "net"
    )
    
    func localAddresses() {
        ifaces, err := net.Interfaces()
        if err != nil {
            fmt.Print(fmt.Errorf("localAddresses: %+v\n", err.Error()))
            return
        }
        for _, i := range ifaces {
            addrs, err := i.Addrs()
            if err != nil {
                fmt.Print(fmt.Errorf("localAddresses: %+v\n", err.Error()))
                continue
            }
            for _, a := range addrs {
                switch v := a.(type) {
                case *net.IPAddr:
                    fmt.Printf("%v : %s (%s)\n", i.Name, v, v.IP.DefaultMask())
    
                case *net.IPNet:
                    fmt.Printf("%v : %s [%v/%v]\n", i.Name, v, v.IP, v.Mask)
                }
    
            }
        }
    }
    
    func main() {
        localAddresses()
    }
    

    【讨论】:

      【解决方案3】:

      这应该会给你正在寻找的ipnet

      ip, ipnet, err := net.ParseCIDR(a.String())
      

      【讨论】:

        【解决方案4】:

        我知道这篇文章适用于 Windows 7,但如果您使用 Mac OS X,希望这对您有所帮助。

        只需致电GetNetMask("en0")

        func GetNetMask(deviceName string) string {
            switch runtime.GOOS {
            case "darwin":
                cmd := exec.Command("ipconfig", "getoption", deviceName, "subnet_mask")
                out, err := cmd.CombinedOutput()
                if err != nil {
                    return ""
                }
        
                nm := strings.Replace(string(out), "\n", "", -1)
                log.Println("netmask=", nm, " OS=", runtime.GOOS)
                return nm
            default:
                return ""
            }
            return ""
        }
        

        【讨论】:

          猜你喜欢
          • 2014-12-31
          • 2015-07-02
          • 1970-01-01
          • 2012-05-17
          • 1970-01-01
          • 1970-01-01
          • 2011-09-08
          • 1970-01-01
          相关资源
          最近更新 更多