Как я могу получить 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
.
Я думаю, что заметил две непосредственные проблемы с вашим кодом:
Псевдоним интерфейса, такой как 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
с некоторыми дополнительными метаданными. Если мы запросим адрес интерфейса на lo
в Go:
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]
Итак, ответ на первую часть вашего вопроса: «используйте имя основного интерфейса». Но есть вторая проблема:
Интерфейсы могут иметь более одного адреса ipv4, но (а) ваш код возвращает только один адрес и (б) ваш код всегда возвращает только первый адрес.
Подходящее решение здесь зависит от того, что вы пытаетесь сделать:
Просто передайте коду явный адрес, а не пытайтесь найти его по имени интерфейса. Так как интерфейс может иметь несколько адресов — и на самом деле это довольно распространенное явление — на самом деле не существует хорошего способа определить «адрес интерфейса».
Думаю, в большинстве случаев это будет лучший вариант.
Найдите (или напишите) код, который может получить все метаданные, связанные с интерфейсом ввода, чтобы вы могли искать конкретный label
.
Просто вызовите ip addr
и проанализируйте вывод. Вы можете получить вывод JSON, позвонив ip -j addr show
.
В Linux вы можете использовать https://github.com/vishvananda/netlink, чтобы получить IP-адреса и метки.
Это библиотека, которая позволяет программе пользовательского пространства Linux взаимодействовать с ядром через интерфейс netlink. Его можно использовать для настройки интерфейсов и маршрутов и т. д. или для получения информации.
В Linux 2.0 существовала концепция псевдонимов, которая формировалась путем добавления двоеточия и строки к имени интерфейса при выполнении ifconfig. Начиная с Linux 2.2, в котором появилась возможность иметь несколько адресов для каждого интерфейса, эта концепция устарела, см. https://www.kernel.org/doc/Documentation/networking/alias.txt
Тем не менее, он обратно совместим, и через интерфейс Netlink вы можете получить атрибуты, включая IFA_LABEL, которые представляют имена интерфейсов (включая псевдонимы).
Поскольку вопрос явно относится к псевдонимам, предполагается, что для каждого имени интерфейса настроен один IP-адрес. Однако при необходимости его можно легко добавить для возврата нескольких IP-адресов на интерфейс, поскольку, конечно, возвращаются полные NetlinkRouteAttr
данные. Тогда это будет охватывать оба случая.
Это может выглядеть примерно так:
package main
import (
"fmt"
"github.com/vishvananda/netlink"
"github.com/vishvananda/netlink/nl"
"net"
"syscall"
)
func main() {
interfaceName := "lo:0"
ip, err := GetInterfaceIpAddr(interfaceName)
if err != nil {
fmt.Println(err)
return
}
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 "", fmt.Errorf("%s not found", interfaceName)
}
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 = 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
Это, вероятно, еще не то, что вам нужно в деталях. Но это может быть первым шагом в правильном направлении.
Удивительный ответ. Именно то, что я хотел.
Решение № 2 было лучшим вариантом для нас. Решение №1 изменит способ развертывания.