Отримайте IP-адресу машини


94

Це запитання майже те саме, що раніше запитували Отримати IP-адресу локального комп'ютера -Попит. Однак мені потрібно знайти IP-адреси Linux-машини .

Отже: Як я - програмно на C ++ - виявляю IP-адреси Linux-сервера, на якому працює моя програма. Сервери матимуть щонайменше дві IP-адреси, і мені потрібна конкретна (одна в даній мережі (загальнодоступна)).

Я впевнений, що для цього є проста функція - але де?


Щоб зробити речі яснішими:

  • Очевидно, сервер матиме "localhost": 127.0.0.1
  • Сервер матиме внутрішню (керуючу) IP-адресу: 172.16.xx
  • Сервер матиме зовнішню (загальнодоступну) IP-адресу: 80.190.xx

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


3
Навіщо позначати попен у ifconfig? Це дійсно правильно робити. Бібліотеки змінюються, але якщоконфігурувати і відкривати завжди будуть.
Ерік Аронесті

3
Ні, ifconfigі routeт. Д. Застарілі для ipкоманди. Тепер вам слід скористатися цим.
Андерс

ifconfig все ще працює на сьогодні більше архітектури, ніж будь-який інший підхід. Тому змініть його на ip command.when / якщо доречно. Попен все-таки краще рішення.
Ерік Аронесті

Відповіді:


104

Я знайшов рішення 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;
}

7
+1 і дякую, що ви використали цей приємний підхід замість тих нудних йоктлів! Однак ваше поводження з IP6 все ще неправильне - вам слід подати на sockaddr_in6, тобто щось на кшталтtmpAddrPtr=&((struct sockaddr_in6 *)ifa->ifa_addr)->sin6_addr;
Андрій

2
@Andrey, спасибі Я оновив свою відповідь (хоча не мав можливості перевірити)
Дванадцять47,

2
дає loта wlan0інтерфейси в моєму випадку. Те , що я вимагаю wlan0або eth0який має інтернет connection.So, я повинен використовувати strcmpабо Автошоу кращий спосіб?
Судіп Бхаттарай

28
  1. Створіть розетку.
  2. Виконайте ioctl(<socketfd>, SIOCGIFCONF, (struct ifconf)&buffer);

Прочитайте /usr/include/linux/if.hінформацію про ifconfта ifreqструктури. Це має дати вам IP-адресу кожного інтерфейсу в системі. Також прочитайте /usr/include/linux/sockios.hдодаткові йокти.


Я мав враження, що він дасть вам лише адресу інтерфейсу, до якого він прив’язаний.
Matt Joiner,

@MattJoiner Ні, погляньте на головну сторінку, SIOCGIFCONF повертає інформацію про всі інтерфейси. Частина сторінки чоловіка, цитується тут: "Поверніть список адрес інтерфейсу (транспортного рівня). Це в даний час означає лише сумісність сімейства AF_INET (IPv4) для сумісності. Користувач передає структуру ifconf як аргумент в ioctl. Він містить вказівник на масив структур ifreq у ifc_req та його довжину в байтах у ifc_len. Ядро заповнює ifreqs усіма поточними адресами інтерфейсу L3, які працюють "
Стефан

25

Мені подобається відповідь jjvainio . Як каже Зан Лникс, він використовує локальну таблицю маршрутизації, щоб знайти IP-адресу інтерфейсу Ethernet, яка буде використана для з'єднання з певним зовнішнім хостом. Використовуючи підключений UDP-розетку, ви можете отримати інформацію, фактично не надсилаючи жодних пакетів. Підхід вимагає вибрати конкретного зовнішнього хоста. Більшу частину часу будь-який відомий громадський ІР повинен робити свою справу. Мені подобається адреса загальнодоступного сервера DNS 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);
}

2
це дає мені приватний ip 127. *. *. *. може тому, що я відстаю від NAT?
Євген

14

Як ви з'ясували, немає такої речі, як "локальна IP-адреса". Ось як дізнатися локальну адресу, яку можна надіслати певному хосту.

  1. Створіть UDP-розетку
  2. Підключіть сокет до зовнішньої адреси (хост, який з часом отримає локальну адресу)
  3. Використовуйте getsockname, щоб отримати локальну адресу

Я ніколи цього не робив, але мені це подобається. Він використовує таблицю маршрутизації ОС автоматично і набагато простіше, ніж сканування списку інтерфейсів.
Зан Лінкс

5
Це передбачає, що ви знаєте, якою буде зовнішня адреса. Це часто не відбувається в центрах обробки даних.
Крістофер

Ви завжди можете спробувати три, які призначені для різних континентів (IANA робить це легко) і прийняти найкращі два з трьох.
Джошуа

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;
}

дякую за це рішення, чудово працює для мене і дуже зручно у багатьох ситуаціях, коли ви просто хочете отримати деякі дані з cl-програм
zitroneneis

Хоча якщоconfig видає потрібні дані, я б сильно не рекомендував використовувати будь-яку програму таким чином. Набагато краще насправді потрапити в джерело ifconfig і отримати функціональність, ніж запустити його і проаналізувати його вихід.
Заур Насібов

1
@MartinMeeser: зробив мову нейтральною, шукаючи окремо "inet" then ":".
Ерік Аронесті

1
Тоді у вас є інші мови, які навіть не набір символів на латині. Рішення полягає в тому, щоб не використовувати командні інструменти, оскільки вони старіють, як ifconfig. Ви повинні використовувати ipзараз. Правильний спосіб генерації мови нейтральний висновок є заданої LANGзмінної оточення C, як:LANG=C ls -l
Anders

1
Дякую. Це просте та дивовижне рішення!
Ахмад Афканде

5

Я не думаю, що є остаточно правильна відповідь на ваше запитання. Натомість існує великий набір способів наблизитись до того, що ви бажаєте. Отже, я дам кілька підказок, як це зробити.

Якщо машина має більше двох інтерфейсів ( 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-адреси. Отже, в LAN не потрібно багато часу, щоб просто пропрасувати їх усі. Це дає вам статистичний підказку про те, де з вашої точки зору знаходиться більшість Інтернету. Однак ви повинні бути трохи розумнішими, ніж знаменитий https://de.wikipedia.org/wiki/SQL_Slammer

ICMP і навіть ARP - хороші джерела для інформації про бічну смугу мережі. Це може допомогти і вам.

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

Цей додатковий список, мабуть, нескінченний і завжди неповний, тому що кожен виробник мережевих пристроїв діловито вигадує нові отвори в безпеці щодо автоматичного виявлення власних пристроїв. Що часто дуже допомагає виявити якийсь публічний інтерфейс, де його не повинно бути.

'Нафф сказав. Вийшов.


4

Крім того, що сказав Стів Бейкер, ви можете знайти опис ioctl SIOCGIFCONF на сторінці користувача netdevice (7) .

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


4

Не варто кодувати це: це те, що може змінитися. Багато програм з'ясовують, до чого слід зв’язати, читаючи в конфігураційному файлі і роблячи все, що там сказано. Таким чином, якщо ваша програма колись у майбутньому повинна прив’язатись до чогось, що не є загальнодоступним IP, вона може це зробити.


2

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

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


1

Ви можете зробити якусь інтеграцію з curl як щось таке просте, як: curl www.whatismyip.orgз оболонки ви отримаєте ваш глобальний ip. Ви напевно покладаєтесь на якийсь зовнішній сервер, але завжди будете, якщо стоїте за NAT.


Я ніколи не розумію, чому більше людей не використовують цей сайт. У них навіть є машиночитана сторінка, тому вам не доведеться екранувати лом інформації.
deft_code

1
Можливо, ви не отримаєте правильний IP, якщо ви працюєте всередині проксі.
Джек

1
icanhazip.com повертає IP та нічого більше. Ідеально підходить для вбудовування в баш сценарій!
lukecyca

ipinfo.io/ip - ще одна альтернатива. Якщо ви згортаєте ipinfo.io, він також повертає ім'я хосту, інформацію про геолокацію та інше у форматі JSON.
Бен Даулінг


-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();

}


10
Це важке, лише для Windows (питання про Linux), неправильно відформатоване рішення, що покладається на послуги поза місцевим контролем через крихкий аналіз вмісту веб-сайту. Не вдається знайти жодної позитивної сторони цього "рішення".
віраптор

1
Ха-ха! Синтаксичний розбір HTML навіть шукає коментар, який є попередженням про те, що саме робиться.
нотлеш
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.