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
()
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.