it-swarm.com.ru

Получить IP-адрес машины

Этот вопрос почти такой же, как ранее заданный Получить IP-адрес локального компьютера - Вопрос. Однако мне нужно найти IP-адрес (а) Linux Machine .

Итак: Как мне - программно на C++ - определить IP-адреса сервера linux, на котором запущено мое приложение. Серверы будут иметь как минимум два IP-адреса, и мне нужен конкретный (тот, что в данной сети (общедоступный)).

Я уверен, что для этого есть простая функция - но где?


Чтобы сделать вещи немного яснее:

  • Сервер, очевидно, будет иметь "localhost": 127.0.0.1
  • Сервер будет иметь внутренний (управляющий) IP-адрес: 172.16.x.x
  • Сервер будет иметь внешний (общедоступный) IP-адрес: 80.190.x.x

Мне нужно найти внешний IP-адрес, чтобы привязать к нему мое приложение. Очевидно, что я также могу связываться с INADDR_ANY (и на самом деле это то, что я делаю в данный момент). Я предпочел бы определить публичный адрес, хотя.

88
BlaM

Я обнаружил, что решение ioctl проблематично для os x (которое совместимо с POSIX, поэтому должно быть похоже на linux). Однако getifaddress () позволит вам легко сделать то же самое, он отлично работает для меня на OS X 10,5 и должен быть таким же, как показано ниже.

Ниже я сделал быстрый пример, который напечатает все IPv4-адреса машины (вы также должны проверить, что getifaddrs был успешным, т.е. возвращает 0).

Я обновил его и покажу адреса IPv6.

#include <stdio.h>      
#include <sys/types.h>
#include <ifaddrs.h>
#include <netinet/in.h> 
#include <string.h> 
#include <arpa/inet.h>

int main (int argc, const char * argv[]) {
    struct ifaddrs * ifAddrStruct=NULL;
    struct ifaddrs * ifa=NULL;
    void * tmpAddrPtr=NULL;

    getifaddrs(&ifAddrStruct);

    for (ifa = ifAddrStruct; ifa != NULL; ifa = ifa->ifa_next) {
        if (!ifa->ifa_addr) {
            continue;
        }
        if (ifa->ifa_addr->sa_family == AF_INET) { // check it is IP4
            // is a valid IP4 Address
            tmpAddrPtr=&((struct sockaddr_in *)ifa->ifa_addr)->sin_addr;
            char addressBuffer[INET_ADDRSTRLEN];
            inet_ntop(AF_INET, tmpAddrPtr, addressBuffer, INET_ADDRSTRLEN);
            printf("%s IP Address %s\n", ifa->ifa_name, addressBuffer); 
        } else if (ifa->ifa_addr->sa_family == AF_INET6) { // check it is IP6
            // is a valid IP6 Address
            tmpAddrPtr=&((struct sockaddr_in6 *)ifa->ifa_addr)->sin6_addr;
            char addressBuffer[INET6_ADDRSTRLEN];
            inet_ntop(AF_INET6, tmpAddrPtr, addressBuffer, INET6_ADDRSTRLEN);
            printf("%s IP Address %s\n", ifa->ifa_name, addressBuffer); 
        } 
    }
    if (ifAddrStruct!=NULL) freeifaddrs(ifAddrStruct);
    return 0;
}
98
Twelve47
  1. Создать сокет.
  2. Выполнить ioctl(<socketfd>, SIOCGIFCONF, (struct ifconf)&buffer);

Прочитайте /usr/include/linux/if.h для получения информации о структурах ifconf и ifreq. Это должно дать вам IP-адрес каждого интерфейса в системе. Также прочитайте /usr/include/linux/sockios.h для дополнительных ioctls.

28
Steve Baker

Мне нравится ответ jjvainio . Как говорит Zan Lnyx, он использует локальную таблицу маршрутизации, чтобы найти IP-адрес интерфейса Ethernet, который будет использоваться для соединения с конкретным внешним хостом. Используя подключенный сокет UDP, вы можете получить информацию, не отправляя пакеты. Подход требует, чтобы вы выбрали конкретного внешнего хоста. Большую часть времени любой известный публичный IP должен делать свое дело. Для этой цели мне нравится общедоступный адрес DNS-сервера Google 8.8.8.8, но иногда может потребоваться выбрать другой внешний IP-адрес хоста. Вот некоторый код, который иллюстрирует полный подход.

void GetPrimaryIp(char* buffer, size_t buflen) 
{
    assert(buflen >= 16);

    int sock = socket(AF_INET, SOCK_DGRAM, 0);
    assert(sock != -1);

    const char* kGoogleDnsIp = "8.8.8.8";
    uint16_t kDnsPort = 53;
    struct sockaddr_in serv;
    memset(&serv, 0, sizeof(serv));
    serv.sin_family = AF_INET;
    serv.sin_addr.s_addr = inet_addr(kGoogleDnsIp);
    serv.sin_port = htons(kDnsPort);

    int err = connect(sock, (const sockaddr*) &serv, sizeof(serv));
    assert(err != -1);

    sockaddr_in name;
    socklen_t namelen = sizeof(name);
    err = getsockname(sock, (sockaddr*) &name, &namelen);
    assert(err != -1);

    const char* p = inet_ntop(AF_INET, &name.sin_addr, buffer, buflen);
    assert(p);

    close(sock);
}
24
4dan

Как вы узнали, такого понятия, как "локальный IP-адрес", не существует. Вот как узнать локальный адрес, который можно отправить конкретному хосту.

  1. Создайте сокет UDP
  2. Подключите сокет к внешнему адресу (хост, который в конечном итоге получит локальный адрес)
  3. Используйте getsockname для получения локального адреса
14
jjvainio

Это имеет преимущество работы со многими разновидностями Unix ... и вы можете легко изменить его, чтобы работать с любыми операционными системами. Все приведенные выше решения дают мне ошибки компилятора в зависимости от фазы луны. В тот момент, когда есть хороший способ сделать это в POSIX ... не используйте это (в то время, когда это было написано, это было не так).

// ifconfig | Perl -ne 'print "$1\n" if /inet addr:([\d.]+)/'

#include <stdlib.h>

int main() {
        setenv("LANG","C",1);
        FILE * fp = popen("ifconfig", "r");
        if (fp) {
                char *p=NULL, *e; size_t n;
                while ((getline(&p, &n, fp) > 0) && p) {
                   if (p = strstr(p, "inet ")) {
                        p+=5;
                        if (p = strchr(p, ':')) {
                            ++p;
                            if (e = strchr(p, ' ')) {
                                 *e='\0';
                                 printf("%s\n", p);
                            }
                        }
                   }
              }
        }
        pclose(fp);
        return 0;
}
8
Erik Aronesty

В дополнение к тому, что сказал Стив Бейкер, вы можете найти описание SIOCGIFCONF ioctl на man-страница netdevice (7) .

После того, как у вас есть список всех IP-адресов на хосте, вам придется использовать специфическую логику приложения, чтобы отфильтровать нежелательные адреса и надеяться, что у вас останется один IP-адрес.

4
camh

Не кодируйте его жестко: это может измениться. Многие программы выясняют, с чем связываться, читая файл конфигурации и делая то, что говорит. Таким образом, если ваша программа когда-нибудь в будущем будет привязана к тому, что не является публичным IP, она может это сделать.

4
Thanatos

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

Если у машины более двух интерфейсов (lo считается за один), у вас будут проблемы с автоматическим определением правильного интерфейса. Вот несколько рецептов, как это сделать.

Проблема, например, заключается в том, что хосты находятся в DMZ за брандмауэром NAT, который изменяет публичный IP на какой-то частный IP и пересылает запросы. Ваша машина может иметь 10 интерфейсов, но только один соответствует общедоступному.

Даже автоопределение не работает, если вы используете двойной NAT, где ваш брандмауэр даже переводит исходный IP в нечто совершенно другое. Таким образом, вы даже не можете быть уверены, что маршрут по умолчанию ведет к вашему интерфейсу с открытым интерфейсом.

Обнаружить его по маршруту по умолчанию

Это мой рекомендуемый способ автоматического обнаружения вещей

Что-то вроде ip r get 1.1.1.1 обычно говорит вам интерфейс, который имеет маршрут по умолчанию.

Если вы хотите воссоздать это на своем любимом языке сценариев/программирования, используйте strace ip r get 1.1.1.1 и следуйте по дороге из желтого кирпича.

Установите его с помощью /etc/hosts

Это моя рекомендация, если вы хотите сохранить контроль

Вы можете создать запись в /etc/hosts как

80.190.1.3 publicinterfaceip

Затем вы можете использовать этот псевдоним publicinterfaceip для ссылки на ваш публичный интерфейс.

К сожалению, haproxy не использует этот трюк с IPv6

Использовать окружающую среду

Это хороший обходной путь для /etc/hosts, если вы не root

То же, что /etc/hosts. но используйте для этого окружающую среду. Вы можете попробовать /etc/profile или ~/.profile для этого.

Следовательно, если вашей программе нужна переменная MYPUBLICIP, вы можете включить такой код (это C, не стесняйтесь создавать из него C++):

#define MYPUBLICIPENVVAR "MYPUBLICIP"

const char *mypublicip = getenv(MYPUBLICIPENVVAR);

if (!mypublicip) { fprintf(stderr, "please set environment variable %s\n", MYPUBLICIPENVVAR); exit(3); }

Таким образом, вы можете назвать свой скрипт/программу /path/to/your/script следующим образом

MYPUBLICIP=80.190.1.3 /path/to/your/script

это даже работает в crontab.

Перечислите все интерфейсы и исключите те, которые вам не нужны

Отчаянный путь, если вы не можете использовать ip

Если вы знаете, чего не хотите, вы можете перечислить все интерфейсы и игнорировать все ложные.

Здесь уже, кажется, ответ https://stackoverflow.com/a/265978/490291 для этого подхода.

Делай как DLNA

Путь пьяного человека, который пытается утопиться в алкоголе

Вы можете попытаться перечислить все шлюзы UPnP в вашей сети и таким образом найти правильный маршрут для некоторой "внешней" вещи. Это может быть даже маршрут, на который маршрут по умолчанию не указывает.

Подробнее об этом, возможно, смотрите https://en.wikipedia.org/wiki/Internet_Gateway_Device_Protocol

Это дает хорошее представление о том, какой из них является вашим общедоступным интерфейсом, даже если маршрут по умолчанию указывает на другое место.

Есть еще больше

Где гора встречает пророка

Маршрутизаторы IPv6 объявляют себя, чтобы дать вам правильный префикс IPv6. Просмотр префикса дает вам подсказку о том, имеет ли он внутренний или глобальный IP-адрес.

Вы можете прослушать кадры IGMP или IBGP, чтобы найти подходящий шлюз.

Существует менее 2 ^ 32 IP-адресов. Следовательно, в локальной сети не требуется много времени, чтобы просто пропинговать их всех. Это дает вам статистическую подсказку о том, где находится большая часть Интернета с вашей точки зрения. Однако вы должны быть немного более разумным, чем знаменитый https://de.wikipedia.org/wiki/SQL_Slammer

ICMP и даже ARP являются хорошими источниками информации о боковой полосе сети. Это может помочь вам.

Вы можете использовать широковещательный адрес Ethernet для связи со всеми устройствами сетевой инфраструктуры, которые часто выручают, такими как DHCP (даже DHCPv6) и так далее.

Этот дополнительный список, вероятно, бесконечен и всегда неполон, потому что каждый производитель сетевых устройств активно изобретает новые дыры в безопасности для автоматического обнаружения своих собственных устройств. Что часто очень помогает в обнаружении публичного интерфейса там, где его нет.

'Достаточно. Из.

4
Tino

Вы можете сделать некоторую интеграцию с curl так же просто, как: curl www.whatismyip.org из командной строки получит ваш глобальный ip. Вы как бы зависите от какого-то внешнего сервера, но всегда будете, если находитесь за NAT.

1
Zeist

Поскольку вопрос касается Linux, моя любимая техника для определения IP-адресов машины - использовать netlink. Создав сокет netlink протокола NETLINK_ROUTE и отправив RTM_GETADDR, ваше приложение получит сообщение (я), содержащее все доступные IP-адреса. Пример предоставлен здесь .

Для того, чтобы просто разделить обработку сообщений, libmnl удобен. Если вам интересно узнать больше о различных параметрах NETLINK_ROUTE (и о том, как они анализируются), лучшим источником является исходный код iproute2 (особенно приложения монитора), а также функции приема в ядре. Страница руководства по rtnetlink также содержит полезную информацию.

1
Kristian Evensen

Элегантное решение на основе сценариев для Linux можно найти по адресу: http://www.damnsmalllinux.org/f/topic-3-23-17031-0.html

0
Dave Sieving