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

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

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

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


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

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

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

Ответ 1

Я обнаружил, что решение 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;
}

Ответ 2

  • Создайте сокет.
  • Выполнить ioctl(<socketfd>, SIOCGIFCONF, (struct ifconf)&buffer);

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

Ответ 3

Мне нравится jjvainio answer. Как говорит Zan Lnyx, он использует локальную таблицу маршрутизации, чтобы найти IP-адрес интерфейса ethernet, который будет использоваться для подключения к определенному внешнему хосту. Используя подключенный UDP-сокет, вы можете получить информацию без фактической отправки каких-либо пакетов. Подход требует, чтобы вы выбрали конкретный внешний хост. В большинстве случаев любой известный публичный IP-адрес должен делать трюк. Для этой цели мне нравится адрес 8.8.8.8 общего DNS-сервера Google, но иногда вы можете выбрать другой внешний 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);
}

Ответ 4

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

  • Создание сокета UDP
  • Подключите сокет к внешнему адресу (хост, который в конечном итоге получит локальный адрес)
  • Используйте getockname для получения локального адреса

Ответ 5

У этого есть преимущество в работе над многими вариантами unix... и вы можете модифицировать его тривиально, чтобы работать с любыми o/s. Все вышеперечисленные решения дают мне ошибки компилятора в зависимости от фазы луны. В тот момент, когда хороший способ 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;
}

Ответ 6

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

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

Ответ 7

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

Ответ 8

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

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

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

Даже автоопределение не работает, если вы находитесь на двойном NAT, где ваш брандмауэр даже переводит source-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, не стесняйтесь создавать С++ из него):

#define MYPUBLICIPENVVAR "MYPUBLICIP"

const char *mypublicip = getenv(MYPUBLICIPENVVAR);

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

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

MYPUBLICIP=80.190.1.3 /path/to/your/script

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

Перечислить все интерфейсы и устранить те, которые вы не хотите

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

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

Здесь уже представляется ответ fooobar.com/questions/68605/... для этого подхода.

Сделайте это как DLNA

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

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

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

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

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

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

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

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

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

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

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

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

'Нафф сказал. Out.

Ответ 9

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

Ответ 10

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

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

Ответ 12

// Использовать HTTP-запрос к хорошо известному серверу, который отсылает общедоступный IP-адрес void GetPublicIP (CString и csIP) {   // Инициализировать COM   bool bInit = false;   if (SUCCEEDED (CoInitialize (NULL)))   {       //COM был инициализирован       bInit = true;

 //Создание объекта запроса HTTP   MSXML2:: IXMLHTTPRequestPtr HTTPRequest;   HRESULT hr = HTTPRequest.CreateInstance( "MSXML2.XMLHTTP" );   if (SUCCEEDED (hr))   {       // Создаем запрос на веб-сайт, который возвращает общедоступный IP-адрес       VARIANT Async;       Async.vt = VT_BOOL;       Async.boolVal = VARIANT_FALSE;       CComBSTR ccbRequest = L "http://whatismyipaddress.com/";
       // Открыть запрос       if (SUCCEEDED (HTTPRequest- > raw_open (L "GET", ccbRequest, Async)))       {           // Отправить запрос           if (SUCCEEDED (HTTPRequest- > raw_send()))           {               // Получить ответ               CString csRequest = HTTPRequest- > GetresponseText();
               // Разбираем IP-адрес               CString csMarker = "<! - свяжитесь с нами перед использованием script, чтобы получить ваш IP-адрес - >";               int iPos = csRequest.Find(csMarker);               if (iPos == -1)                   вернуть;               iPos + = csMarker.GetLength();               int iPos2 = csRequest.Find(csMarker, iPos);               if (iPos2 == -1)                   вернуть;
               // Создаем IP-адрес               int nCount = iPos2 - iPos;               csIP = csRequest.Mid(iPos, nCount);           }       }   }
}

//Unitialize  COM
if (bInit)   CoUninitialize();
Код>

}