Go语言 获取虚拟网络接口的IP地址

sycxhyv7  于 2023-01-06  发布在  Go
关注(0)|答案(2)|浏览(233)

如何获取虚拟网络接口的IP地址?这是一个如下所示的接口:

lo:0: flags=73<UP,LOOPBACK,RUNNING>  mtu 65536
        inet 192.168.40.1  netmask 255.255.255.255
        loop  txqueuelen 1000  (Local Loopback)

这是我如何检索常规接口的IP地址:

func GetInterfaceIpAddr(interfaceName string) (string, error) {
    var (
        ief      *net.Interface
        addrs    []net.Addr
        ipv4Addr net.IP
    )

    ief, err := net.InterfaceByName(interfaceName)

    if err != nil { // get interface
        log.Info("InterfaceByName failed")
        return "", err
    }

    addrs, err = ief.Addrs()

    if err != nil {
        return "", err
    }

    for _, addr := range addrs { // get ipv4 address
        if ipv4Addr = addr.(*net.IPNet).IP.To4(); ipv4Addr != nil {
            break
        }
    }
    if ipv4Addr == nil {
        return "", errors.New(fmt.Sprintf("interface %s doesn't have an ipv4 address\n", interfaceName))
    }

    return ipv4Addr.String(), nil
}

当我将lo:0传递给上面的函数时,net.InterfaceByName失败,并显示以下错误:route ip+net: no such network interface.

tcomlyy6

tcomlyy61#

我想我发现了您的代码中的两个直接问题:
1.接口别名如lo:0不是一个"虚拟接口",它只是一个标签应用到一个地址。你会发现地址与主接口相关联(在本例中为lo)。ifconfig命令的输出是误导性的,你不应该使用它;使用ip addr查看接口地址配置。
例如,如果我使用ifconfig创建一个"别名接口",如下所示:

# ifconfig lo:0 192.168.123.123/24

我看到ifconfig的以下输出:

# ifconfig
lo: flags=73<UP,LOOPBACK,RUNNING>  mtu 65536
        inet 127.0.0.1  netmask 255.0.0.0
        inet6 ::1  prefixlen 128  scopeid 0x10<host>
        loop  txqueuelen 1000  (Local Loopback)
        RX packets 504291  bytes 72010889 (68.6 MiB)
        RX errors 0  dropped 0  overruns 0  frame 0
        TX packets 504291  bytes 72010889 (68.6 MiB)
        TX errors 0  dropped 0 overruns 0  carrier 0  collisions 0

lo:0: flags=73<UP,LOOPBACK,RUNNING>  mtu 65536
        inet 192.168.123.123  netmask 255.255.255.0
        loop  txqueuelen 1000  (Local Loopback)

ip addr告诉我:

# ip addr show lo
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1000
    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
    inet 127.0.0.1/8 scope host lo
       valid_lft forever preferred_lft forever
    inet 192.168.123.123/24 scope global lo:0
       valid_lft forever preferred_lft forever
    inet6 ::1/128 scope host 
       valid_lft forever preferred_lft forever

在这里你可以看到,辅助地址实际上是通过一些额外的元数据与设备lo相关联的,如果我们在Go语言中请求lo的接口地址:

ief, _ := net.InterfaceByName("lo")
addrs, _ := ief.Addrs()
fmt.Printf("addrs: %v\n", addrs)

我们得到:

addrs: [127.0.0.1/8 192.168.123.123/24 ::1/128]

所以你问题第一部分的答案是"使用主接口名"。但是还有第二个问题:
1.接口可以有多个ipv4地址,但是(a)你的代码只返回一个地址,(b)你的代码只返回第一个地址。
这里合适的解决方案取决于您尝试做什么:

  • 只要给你的代码传递一个显式地址,而不是试图从接口名称中发现它。因为一个接口可以有多个地址--事实上这是相对常见的--没有一个真正的好方法来确定"接口的地址"。

我认为在大多数情况下这将是最好的选择。

  • 查找(或编写)可以获取与in接口关联的所有元数据的代码,以便查找特定的label
  • 只需调用ip addr并解析输出,就可以通过调用ip -j addr show获得JSON输出。
h7wcgrx3

h7wcgrx32#

在Linux上,您可以使用https://github.com/vishvananda/netlink来获取IP地址和标签。
这可能看起来像这样:

package main

import (
    "errors"
    "fmt"
    "github.com/vishvananda/netlink"
    "github.com/vishvananda/netlink/nl"
    "net"
    "syscall"
)

func main() {
    interfaceName := "lo:0"
    ip, err := GetInterfaceIpAddr(interfaceName)
    if err != nil {
        panic(err)
    }
    fmt.Printf("%s -> %s\n", interfaceName, ip)
}

func GetInterfaceIpAddr(interfaceName string) (string, error) {
    ifis, err := interfaces(netlink.FAMILY_V4)
    if err != nil {
        return "", err
    }
    ip, ok := ifis[interfaceName]
    if !ok {
        return "", errors.New("not found")
    }
    return ip.String(), nil
}

func interfaces(family int) (map[string]net.IP, error) {
    req := nl.NewNetlinkRequest(syscall.RTM_GETADDR, syscall.NLM_F_DUMP)
    msg := nl.NewIfInfomsg(family)
    req.AddData(msg)
    messages, err := req.Execute(syscall.NETLINK_ROUTE, syscall.RTM_NEWADDR)
    if err != nil {
        return nil, err
    }
    ifis := make(map[string]net.IP)
    for _, m := range messages {
        msg := nl.DeserializeIfAddrmsg(m)
        attrs, err := nl.ParseRouteAttr(m[msg.Len():])
        if err != nil {
            return nil, err
        }
        var ip net.IP
        var label string
        for _, attr := range attrs {
            switch attr.Attr.Type {
            case syscall.IFA_LOCAL:
                ip = net.IP(attr.Value)
            case syscall.IFA_LABEL:
                label = string(attr.Value[:len(attr.Value)-1])
            }
        }
        if ip != nil && label != "" {
            ifis[label] = ip
        }
    }
    return ifis, nil
}

Ubuntu上的测试给出:

lo:0 -> 127.0.0.2

这可能还不是你所需要的细节,但它可能是朝着正确方向迈出的第一步。

相关问题