LINUX.ORG.RU
решено ФорумAdmin

Подскажите надёжный способ определить свой локальный IP-адрес в скрипте

 , ,


1

2

Сабж. Нужно определить свой IP-адрес (и, хорошо бы, имя интерфейса), с которого машина выходит в интернет, т.е. который находится в одной подсети с шлюзом по-умолчанию.
Сделать это нужно внутри скрипта (т.е. не глазами посмотреть). Как надёжнее и правильнее всего?

★★★

Последнее исправление: gasinvein (всего исправлений: 3)

Ответ на: комментарий от gasinvein

Адрес на машине узнаешь из ifconfig.
Адрес который выдал тебе провайдер - на роутере.
Адрес, с которого ты виден в интернете - на веб-сервисах типа internet.yandex.ru.

anonymous
()
Ответ на: комментарий от anonymous

Ты меня дураком считаешь или сам читать не умеешь?

это нужно внутри скрипта (т.е. не глазами посмотреть)

gasinvein ★★★
() автор топика

Не знаю правильно это будет или нет, но есть вариант такой:

traceroute 8.8.8.8 | grep чего-то нужное.

IP интерфейса через который машина реально идет в инет ты точно сможешь получить. А имя интерфейса зная адрес можно получить откуда угодно. Хоть из ifconfig.

Это не решение, а направление куда копать если других вариантов не будет.

AfterWork
()

Я бы брал вывод команд

ip route list
ip addr list
и далее sed-ом по регулярному выражению вырезал то, что нужно.

rumgot ★★★★★
()
Ответ на: комментарий от gasinvein

Ну так и смотри внутри скрипта. Только адрес машины и адрес под которым ты виден в инете, могут быть 2 разных

anonymous
()
Ответ на: комментарий от pvvking

А в выводе ip точно постоянное количество полей? Не окажется ли в $3 и $5 что-то другое если, например, маршрута по-умолчанию нет?

gasinvein ★★★
() автор топика
Ответ на: комментарий от pvvking

Ну вот я потому и спросил про надёжный способ. Парсинг человекочитаемого вывода программ мне таковым не кажется.
Делают же это как-то в высокоуровневых решениях типа NetworkManager'а.
Например, mac-адрес можно прочитать из /sys/class/net/$ifname/address. А для IP есть какой-нибудь подобный интерфейс?

gasinvein ★★★
() автор топика
Последнее исправление: gasinvein (всего исправлений: 2)

Если шлюз у тебя один и никаких nexthop то

INTERFACE=`ip route list | grep default | cut -f 5 -d ' '`
ADDRESS=`ip addr list | grep -o "inet .* ${INTERFACE}" | cut -f 2 -d ' '`
Stanson ★★★★★
()
Ответ на: комментарий от gasinvein

Там это делают через netlink. В баше ты так данные не достанешь, но возможно что-то есть и в /sys и /proc.

Я бы сделал парсинг вывода ip -o route get

Deleted
()
Ответ на: комментарий от gasinvein

Ну можешь притащить биндинги питона к нетлинку, но это не самое минималистичное решение

Deleted
()
Ответ на: комментарий от gasinvein
import os; os.popen("""ip route get 8.8.8.8 |grep dev|awk '{print $3" "$5}'""").read()

Можешь не благодарить.

entefeed ☆☆☆
()

копать куда-то сюда: ip a| grep `ip r | grep def | awk '{print $5}'`

хотя с ip route get выглядит интереснее - там сразу src есть...

AS ★★★★★
()
Последнее исправление: AS (всего исправлений: 2)
Ответ на: комментарий от gasinvein

Парсинг человекочитаемого вывода программ мне таковым не кажется.

Тогда тебе нужен либо netlink socket, либо ioctl(...SIOC*) и сишечка/перл/пистон.

Stanson ★★★★★
()
Ответ на: комментарий от gasinvein

Блин, вроде типовая задача же. Неужели не придумали ничего лучше, чем парсить выхлоп утилит?

А ничего лучше и не придумаешь. Можно ещё парсить содержимое /proc/net/route например

Stanson ★★★★★
()
#!/usr/bin/python
from socket import AF_INET, AF_INET6, inet_ntop
from ctypes import (
    Structure, Union, POINTER, 
    pointer, get_errno, cast,
    c_ushort, c_byte, c_void_p, c_char_p, c_uint, c_int, c_uint16, c_uint32
)
import ctypes.util
import ctypes

class struct_sockaddr(Structure):
     _fields_ = [
        ('sa_family', c_ushort),
        ('sa_data', c_byte * 14),]

class struct_sockaddr_in(Structure):
    _fields_ = [
        ('sin_family', c_ushort),
        ('sin_port', c_uint16),
        ('sin_addr', c_byte * 4)]

class struct_sockaddr_in6(Structure):
    _fields_ = [
        ('sin6_family', c_ushort),
        ('sin6_port', c_uint16),
        ('sin6_flowinfo', c_uint32),
        ('sin6_addr', c_byte * 16),
        ('sin6_scope_id', c_uint32)]

class union_ifa_ifu(Union):
    _fields_ = [
        ('ifu_broadaddr', POINTER(struct_sockaddr)),
        ('ifu_dstaddr', POINTER(struct_sockaddr)),]

class struct_ifaddrs(Structure):
    pass

struct_ifaddrs._fields_ = [
    ('ifa_next', POINTER(struct_ifaddrs)),
    ('ifa_name', c_char_p),
    ('ifa_flags', c_uint),
    ('ifa_addr', POINTER(struct_sockaddr)),
    ('ifa_netmask', POINTER(struct_sockaddr)),
    ('ifa_ifu', union_ifa_ifu),
    ('ifa_data', c_void_p),]

libc = ctypes.CDLL(ctypes.util.find_library('c'))

def ifap_iter(ifap):
    ifa = ifap.contents
    while True:
        yield ifa
        if not ifa.ifa_next:
            break
        ifa = ifa.ifa_next.contents

def getfamaddr(sa):
    family = sa.sa_family
    addr = None
    if family == AF_INET:
        sa = cast(pointer(sa), POINTER(struct_sockaddr_in)).contents
        addr = inet_ntop(family, sa.sin_addr)
    elif family == AF_INET6:
        sa = cast(pointer(sa), POINTER(struct_sockaddr_in6)).contents
        addr = inet_ntop(family, sa.sin6_addr)
    return family, addr

class NetworkInterface(object):
    def __init__(self, name):
        self.name = name
        self.index = libc.if_nametoindex(name)
        self.addresses = {}
    def __str__(self):
        return "%s [index=%d, IPv4=%s, IPv6=%s]" % (
            self.name, self.index,
            self.addresses.get(AF_INET),
            self.addresses.get(AF_INET6))

def get_network_interfaces():
    ifap = POINTER(struct_ifaddrs)()
    result = libc.getifaddrs(pointer(ifap))
    if result != 0:
        raise OSError(get_errno())
    del result
    try:
        retval = {}
        for ifa in ifap_iter(ifap):
            name = ifa.ifa_name
            i = retval.get(name)
            if not i:
                i = retval[name] = NetworkInterface(name)
            try:
                family, addr = getfamaddr(ifa.ifa_addr.contents)
            except ValueError:
                family, addr = None, None
            if addr:
                i.addresses[family] = addr
        return retval.values()
    finally:
        libc.freeifaddrs(ifap)

if __name__ == '__main__':
    print [str(ni) for ni in get_network_interfaces()]
pvvking ★★
()

находится в одной подсети с шлюзом по-умолчанию

(тред не читал) тут уже сказали, что шлюзов по умолчанию может быть несколько?

sergej ★★★★★
()
Ответ на: комментарий от gasinvein

ip route get connected 8.8.8.8 | grep -oP 'src \d+\.\d+\.\d+\.\d+'

дальше сам

sergej ★★★★★
()
Ответ на: комментарий от pvvking

Совсем сложно. Проще уж в самом деле поставить NetworkManager и дёргать его dbus-интерфейс.

gasinvein ★★★
() автор топика

Можешь взять libnl и через него вытащить, хз есть ли байндинги к питону на него.

anonymous
()
Ответ на: комментарий от pvvking

Если ты юзаешь awk, зачем тебе grep?

ip route get 8.8.8.8 | awk '/dev/{print $3 " " $5}'

Но лично в моём случае $3 — адрес DHCP-сервера, а локальный IP — $7:

ip route get 8.8.8.8 | awk '/dev/{print $5 " " $7}'
r3lgar ★★★★★
()
Ответ на: комментарий от gasinvein

В линуксе всё делается парсингом выхлопа программ, на том он и стоит. Философия павершелла не прижилась. Поэтому только так, становись мастером авка и наслаждайся текстом. Популярные утилиты обычно свой выхлоп не меняют.

Legioner ★★★★★
()
Последнее исправление: Legioner (всего исправлений: 1)
Ответ на: комментарий от gasinvein

тогда пиши свой велосипед, уровнем ниже. Если парсить не хочешь. Будет так как ты желаешь.

anonymous
()
package main

import (
    "fmt"
    "net"
    "os"
)

func main() {
    addrs, err := net.InterfaceAddrs()
    if err != nil {
        fmt.Printf("Error: %s\n", err.Error())
        os.Exit(1)
    }

    for _, addr := range addrs {
        if ipnet, ok := addr.(*net.IPNet); ok && !ipnet.IP.IsLoopback() {
            if ipnet.IP.To4() != nil {
                fmt.Println(ipnet.IP)
            }
        }
    }
}

Получаем:

192.168.50.46
172.21.0.1
172.17.0.1
172.19.0.1

Для получения таблицы маршрутизации есть netlink library

paganmind
()
Ответ на: комментарий от r3lgar

Но лично в моём случае

Вот поэтому полагаться на парсинг выхлопа и не хочется.

gasinvein ★★★
() автор топика

Если бы это был OpenBSD, то просто ifconfig egress.

beastie ★★★★★
()

который находится в одной подсети с шлюзом по-умолчанию.

Если тебе нужен интерфейс шлюза по-умолчанию, то route -n легко это покажет:

$ route -n | grep '^0\.0\.0\.0' | sed 's/^.* \([^ ]*\)$/\1/g'
eth0

Степень надежности ограничивается только вариациями формата вывода route (например, на cygwin не так).

Только не во всех случаях это будет интерфейс, через который происходит выход в Интернет. Но тебе твой кейс виднее.

Kroz ★★★★★
()
Ответ на: комментарий от gasinvein

Тогда уж ip route get 8.8.8.8 | awk ..., но мне это хорошим решением не кажется.

Не суть ip route get или traceroute ни то ни другое не есть хорошее решение. ИМХО.

Мы упираемся в выяснение того где именно идет инет. Остальное добыть не проблема.

AfterWork
()
Ответ на: комментарий от gasinvein

Блин, вроде типовая задача же.

Не сильно то она и типовая. Я вот ни разу не сталкивался с тем что надо именно выяснить через где машина в инет ходит.

AfterWork
()

У тебя вообще есть какие-то фомальные данные или только то что ты сказал и нужно именно универсальное решение?

AfterWork
()
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.