2008-10-17 4 views
82

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

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

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


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

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

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

+3

Зачем отмечать popen ifconfig? Это действительно правильно. Библиотеки меняются, но ifconfig и popen всегда будут там. – 2011-05-18 13:40:37

+2

Нет, `ifconfig`,` route` и т. Д. Устарели для команды `ip`. Теперь вы должны использовать это вместо этого. – Anders 2014-09-12 22:58:14

+0

ifconfig по-прежнему, работает над большим количеством архитектур, чем любой другой подход. Поэтому измените его на ip command.when/если это необходимо. popen все еще лучшее решение. – 2016-09-13 12:54:02

ответ

26
  1. Создать розетку.
  2. Выполните ioctl(<socketfd>, SIOCGIFCONF, (struct ifconf)&buffer);

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

+0

Я был под впечатлением, что он даст вам только адрес интерфейса, к которому он привязан. – 2011-03-12 07:06:58

+0

@MattJoiner Нет, взгляните на страницу руководства, SIOCGIFCONF вернет информацию обо всех интерфейсах. Часть справочной страницы, приведенной здесь: «Верните список адресов интерфейса (транспортного уровня). В настоящее время это означает только адреса семейства AF_INET (IPv4) для совместимости. Пользователь передает структуру ifconf в качестве аргумента для ioctl. указатель на массив структур ifreq в ifc_req и его длину в байтах в ifc_len. Ядро заполняет ifreqs всеми текущими адресами интерфейса L3, которые работают « – 2017-03-26 00:44:53

1

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

+0

Я никогда не понимаю, почему больше людей не используют этот сайт. У них даже есть машиночитаемая страница, поэтому вам не нужно отображать информацию об утилизации. – 2009-08-29 00:12:16

+1

Возможно, вы не получите правильный IP-адрес, если работаете внутри прокси-сервера. – Jack 2010-07-12 05:38:37

+1

http://icanhazip.com возвращает IP-адрес и ничего больше. Идеально подходит для встраивания в скрипт bash! – lukecyca 2011-03-16 20:42:24

4

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

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

11

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

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

Я нашел Решение 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; 
} 
21

Мне нравится jjvainio's answer. Как говорит 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); 
} 
4

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

-10

// Use a HTTP request to a well known server that echo's back the public IP address void GetPublicIP(CString & csIP) { // Initialize COM bool bInit = false; if (SUCCEEDED(CoInitialize(NULL))) { // COM was initialized bInit = true;

// Create a HTTP request object 
    MSXML2::IXMLHTTPRequestPtr HTTPRequest; 
    HRESULT hr = HTTPRequest.CreateInstance("MSXML2.XMLHTTP"); 
    if (SUCCEEDED(hr)) 
    { 
     // Build a request to a web site that returns the public IP address 
     VARIANT Async; 
     Async.vt = VT_BOOL; 
     Async.boolVal = VARIANT_FALSE; 
     CComBSTR ccbRequest = L"http://whatismyipaddress.com/"; 

     // Open the request 
     if (SUCCEEDED(HTTPRequest->raw_open(L"GET",ccbRequest,Async))) 
     { 
      // Send the request 
      if (SUCCEEDED(HTTPRequest->raw_send())) 
      { 
       // Get the response 
       CString csRequest = HTTPRequest->GetresponseText(); 

       // Parse the IP address 
       CString csMarker = "<!-- contact us before using a script to get your IP address -->"; 
       int iPos = csRequest.Find(csMarker); 
       if (iPos == -1) 
        return; 
       iPos += csMarker.GetLength(); 
       int iPos2 = csRequest.Find(csMarker,iPos); 
       if (iPos2 == -1) 
        return; 

       // Build the IP address 
       int nCount = iPos2 - iPos; 
       csIP = csRequest.Mid(iPos,nCount); 
      } 
     } 
    } 
} 

// Unitialize COM 
if (bInit) 
    CoUninitialize(); 

}

9

Это имеет преимущество работы на многих разновидностях 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; 
} 
1

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

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

4

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

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

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

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

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

Это мой рекомендуемый способ AUTODETECT вещи

Что-то вроде 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

Same как /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-адресов. Следовательно, LAN не требует много времени, чтобы просто пинговать их все. Это дает вам статистический намек на то, где большая часть Интернета находится с вашей точки зрения. Однако вы должны быть немного более разумными, чем знаменитые https://de.wikipedia.org/wiki/SQL_Slammer

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

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

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

'Нафф сказал. Вне.

Смежные вопросы