Ottieni l'indirizzo IP della macchina


94

Questa domanda è quasi la stessa della domanda precedente Ottieni l'indirizzo IP del computer locale . Tuttavia, ho bisogno di trovare gli indirizzi IP di una macchina Linux .

Quindi: come faccio - a livello di programmazione in C ++ - a rilevare gli indirizzi IP del server Linux su cui è in esecuzione la mia applicazione. I server avranno almeno due indirizzi IP e ne ho bisogno di uno specifico (quello in una data rete (quella pubblica)).

Sono sicuro che ci sia una semplice funzione per farlo, ma dove?


Per rendere le cose un po 'più chiare:

  • Il server ovviamente avrà il "localhost": 127.0.0.1
  • Il server avrà un indirizzo IP interno (di gestione): 172.16.xx
  • Il server avrà un indirizzo IP esterno (pubblico): 80.190.xx

Devo trovare l'indirizzo IP esterno per collegarvi la mia applicazione. Ovviamente posso anche associarmi a INADDR_ANY (e in realtà è quello che faccio al momento). Tuttavia, preferirei rilevare l'indirizzo pubblico.


3
Perché segnare il popen di ifconfig? È davvero la cosa giusta da fare. Le librerie cambiano, ma ifconfig e popen saranno sempre presenti.
Erik Aronesty

3
No, ifconfig, routeecc sono obsoleti per il ipcomando. Ora dovresti usarlo invece.
Anders

ifconfig ancora, oggi, lavora su più architetture di qualsiasi altro approccio. Quindi modificalo nel comando ip. Quando / se appropriato. popen è ancora la soluzione migliore.
Erik Aronesty

Risposte:


104

Ho trovato la soluzione ioctl problematica su os x (che è conforme a POSIX quindi dovrebbe essere simile a linux). Tuttavia getifaddress () ti consente di fare la stessa cosa facilmente, funziona bene per me su os x 10.5 e dovrebbe essere lo stesso sotto.

Ho fatto un rapido esempio di seguito che stamperà tutto l'indirizzo IPv4 della macchina, (dovresti anche controllare che getifaddrs abbia avuto successo, cioè restituisce 0).

L'ho aggiornato per mostrare anche gli indirizzi 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 e grazie per aver portato questo simpatico approccio invece di quei noiosi ioctl! Tuttavia, la tua gestione dell'IP6 è ancora errata: dovresti trasmettere a sockaddr_in6, cioè qualcosa cometmpAddrPtr=&((struct sockaddr_in6 *)ifa->ifa_addr)->sin6_addr;
Andrey

2
@Andrey, grazie. Ho aggiornato la mia risposta (non ho avuto la possibilità di testarla però)
Twelve47

2
loe wlan0interfacce nel mio caso. Quello di cui ho bisogno è wlan0o eth0che abbia una connessione Internet, quindi dovrei usare strcmpo c'è un modo migliore?
Sudip Bhattarai

28
  1. Crea un socket.
  2. Eseguire ioctl(<socketfd>, SIOCGIFCONF, (struct ifconf)&buffer);

Leggi /usr/include/linux/if.hper informazioni sulle strutture ifconfe ifreq. Questo dovrebbe darti l'indirizzo IP di ogni interfaccia sul sistema. Leggi anche /usr/include/linux/sockios.hper ulteriori ioctl.


Avevo l'impressione che ti fornisse solo l'indirizzo dell'interfaccia a cui si lega.
Matt Joiner

@MattJoiner No, dai un'occhiata alla pagina man, SIOCGIFCONF restituisce informazioni su tutte le interfacce. Parte della pagina man citata qui: "Restituisce un elenco di indirizzi di interfaccia (livello di trasporto). Questo attualmente significa solo indirizzi della famiglia AF_INET (IPv4) per compatibilità. L'utente passa una struttura ifconf come argomento a ioctl. Contiene un puntatore a un array di strutture ifreq in ifc_req e la sua lunghezza in byte in ifc_len. Il kernel riempie gli ifreq con tutti gli indirizzi dell'interfaccia L3 correnti che sono in esecuzione "
Stéphane

25

Mi piace la risposta di jjvainio . Come dice Zan Lnyx, utilizza la tabella di routing locale per trovare l'indirizzo IP dell'interfaccia ethernet che verrebbe utilizzata per una connessione a uno specifico host esterno. Utilizzando un socket UDP connesso, è possibile ottenere le informazioni senza effettivamente inviare alcun pacchetto. L'approccio richiede la scelta di un host esterno specifico. Il più delle volte, qualsiasi IP pubblico noto dovrebbe fare il trucco. Mi piace l'indirizzo del server DNS pubblico di Google 8.8.8.8 per questo scopo, ma a volte potresti voler scegliere un IP host esterno diverso. Di seguito è riportato del codice che illustra l'approccio completo.

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
questo mi dà un ip privato 127. *. *. *. forse perché sono dietro NAT?
eugene

14

Come hai scoperto non esiste un unico "indirizzo IP locale". Ecco come scoprire l'indirizzo locale che può essere inviato a un host specifico.

  1. Crea un socket UDP
  2. Connetti il ​​socket a un indirizzo esterno (l'host che alla fine riceverà l'indirizzo locale)
  3. Usa getsockname per ottenere l'indirizzo locale

Non l'ho mai fatto in questo modo ma mi piace. Utilizza automaticamente la tabella di instradamento del sistema operativo ed è molto più semplice della scansione dell'elenco delle interfacce.
Zan Lynx

5
Ciò presuppone che tu sappia quale sarà un indirizzo esterno. Questo spesso non è il caso dei data center.
Christopher,

Puoi sempre provarne tre assegnati a continenti diversi (IANA lo rende facile) e accettare i migliori due su tre.
Joshua

9

Questo ha il vantaggio di lavorare su molte versioni di unix ... e puoi modificarlo banalmente per lavorare su qualsiasi o / s. Tutte le soluzioni sopra mi danno errori del compilatore a seconda della fase lunare. Nel momento in cui c'è un buon modo POSIX per farlo ... non usarlo (al momento in cui è stato scritto, non era così).

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

grazie per questa soluzione, funziona alla grande per me ed è molto utile in molte situazioni in cui vuoi solo ottenere alcuni dati da programmi cl
zitroneneis

Sebbene ifconfig restituisca i dati richiesti, sconsiglierei vivamente di utilizzare qualsiasi programma in questo modo. È molto meglio entrare effettivamente nel sorgente di ifconfig e ottenere la funzionalità piuttosto che eseguirlo e analizzarne l'output.
Zaur Nasibov

1
@ MartinMeeser: ha reso il linguaggio neutro cercando "inet" e poi ":" separatamente.
Erik Aronesty

1
Quindi hai altre lingue che non sono nemmeno un set di caratteri basato sul latino. La soluzione è di non utilizzare strumenti di comando, poiché vengono deprecati come ifconfig. Dovresti usare ipadesso. Il modo corretto di generare un output indipendente dalla lingua è impostare LANGla variabile di ambiente su C, come:LANG=C ls -l
Anders

1
Grazie. È una soluzione semplice e fantastica!
Ahmad Afkandeh

5

Non credo che ci sia una risposta definitiva e giusta alla tua domanda. Invece c'è un grande pacchetto di modi per avvicinarsi a ciò che desideri. Quindi fornirò alcuni suggerimenti su come farlo.

Se una macchina ha più di 2 interfacce ( loconta come una) avrai problemi a rilevare facilmente l'interfaccia giusta. Ecco alcune ricette su come farlo.

Il problema, ad esempio, è se gli host si trovano in una DMZ dietro un firewall NAT che cambia l'IP pubblico in un IP privato e inoltra le richieste. La tua macchina può avere 10 interfacce, ma solo una corrisponde a quella pubblica.

Anche il rilevamento automatico non funziona nel caso in cui tu sia su double-NAT, dove il tuo firewall traduce persino l'IP sorgente in qualcosa di completamente diverso. Quindi non puoi nemmeno essere sicuro che il percorso predefinito porti alla tua interfaccia con un'interfaccia pubblica.

Rilevalo tramite il percorso predefinito

Questo è il mio modo consigliato per rilevare automaticamente le cose

Qualcosa come di ip r get 1.1.1.1solito ti dice l'interfaccia che ha il percorso predefinito.

Se vuoi ricrearlo nel tuo linguaggio di scripting / programmazione preferito, usa strace ip r get 1.1.1.1e segui la strada di mattoni gialli.

Impostalo con /etc/hosts

Questa è la mia raccomandazione se vuoi mantenere il controllo

Puoi creare una voce in /etc/hostslike

80.190.1.3 publicinterfaceip

Quindi puoi usare questo alias publicinterfaceipper fare riferimento alla tua interfaccia pubblica.

Purtroppo haproxynon risolve questo trucco con IPv6

Usa l'ambiente

Questa è una buona soluzione /etc/hostsnel caso in cui non lo seiroot

Uguale a /etc/hosts. ma usa l'ambiente per questo. Puoi provare /etc/profileo ~/.profileper questo.

Quindi, se il tuo programma ha bisogno di una variabile MYPUBLICIP, puoi includere codice come (questo è C, sentiti libero di creare C ++ da esso):

#define MYPUBLICIPENVVAR "MYPUBLICIP"

const char *mypublicip = getenv(MYPUBLICIPENVVAR);

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

Quindi puoi chiamare il tuo script / programma in /path/to/your/scriptquesto modo

MYPUBLICIP=80.190.1.3 /path/to/your/script

funziona anche in crontab.

Enumera tutte le interfacce ed elimina quelle che non desideri

Il modo disperato se non puoi usare ip

Se sai cosa non vuoi, puoi enumerare tutte le interfacce e ignorare tutte quelle false.

Qui sembra già essere una risposta https://stackoverflow.com/a/265978/490291 per questo approccio.

Fallo come DLNA

La via dell'uomo ubriaco che cerca di affogarsi nell'alcol

Puoi provare a enumerare tutti i gateway UPnP sulla tua rete e in questo modo trovare un percorso appropriato per qualcosa di "esterno". Questo potrebbe anche essere su un percorso in cui il percorso predefinito non punta.

Per ulteriori informazioni su questo forse vedere https://en.wikipedia.org/wiki/Internet_Gateway_Device_Protocol

Questo ti dà una buona impressione su quale sia la tua vera interfaccia pubblica, anche se il tuo percorso predefinito punta altrove.

Ce ne sono anche di più

Dove la montagna incontra il profeta

I router IPv6 si pubblicizzano per darti il ​​giusto prefisso IPv6. Guardare il prefisso ti dà un suggerimento se ha un IP interno o uno globale.

Puoi ascoltare i frame IGMP o IBGP per trovare un gateway adatto.

Ci sono meno di 2 ^ 32 indirizzi IP. Quindi non ci vuole molto su una LAN per eseguire il ping di tutti. Questo ti dà un suggerimento statistico su dove si trova la maggior parte di Internet dal tuo punto di vista. Tuttavia dovresti essere un po 'più ragionevole del famoso https://de.wikipedia.org/wiki/SQL_Slammer

ICMP e persino ARP sono buone fonti per le informazioni sulla banda laterale della rete. Potrebbe aiutarti anche tu.

Puoi utilizzare l'indirizzo di trasmissione Ethernet per contattare tutti i dispositivi dell'infrastruttura di rete che spesso ti aiuteranno, come DHCP (anche DHCPv6) e così via.

Questo elenco aggiuntivo è probabilmente infinito e sempre incompleto, perché ogni produttore di dispositivi di rete è impegnato a inventare nuove falle di sicurezza su come rilevare automaticamente i propri dispositivi. Il che spesso aiuta molto su come rilevare alcune interfacce pubbliche dove non ce ne dovrebbe essere una.

'Nuff ha detto. Su.


4

Oltre a quanto detto da Steve Baker, è possibile trovare una descrizione di SIOCGIFCONF ioctl nella pagina man netdevice (7) .

Una volta ottenuto l'elenco di tutti gli indirizzi IP sull'host, dovrai utilizzare la logica specifica dell'applicazione per filtrare gli indirizzi che non desideri e sperare che ti sia rimasto un indirizzo IP.


4

Non codificarlo: questo è il genere di cose che possono cambiare. Molti programmi capiscono cosa associare leggendo in un file di configurazione e facendo tutto ciò che dice. In questo modo, se il tuo programma in futuro dovesse aver bisogno di legarsi a qualcosa che non è un IP pubblico, può farlo.


2

Poiché la domanda specifica Linux, la mia tecnica preferita per scoprire gli indirizzi IP di una macchina è usare netlink. Creando un socket netlink del protocollo NETLINK_ROUTE e inviando un RTM_GETADDR, la tua applicazione riceverà uno o più messaggi contenenti tutti gli indirizzi IP disponibili. Un esempio è fornito qui .

Per semplificare la gestione dei messaggi, libmnl è conveniente. Se sei curioso di scoprire di più sulle diverse opzioni di NETLINK_ROUTE (e su come vengono analizzate), la migliore fonte è il codice sorgente di iproute2 (specialmente l'applicazione monitor) così come le funzioni di ricezione nel kernel. Anche la pagina man di rtnetlink contiene informazioni utili.


1

Puoi fare un po 'di integrazione con curl come qualcosa di semplice come: curl www.whatismyip.orgdalla shell otterrai il tuo ip globale. Fai affidamento su qualche server esterno, ma lo sarai sempre se sei dietro un NAT.


Non capisco mai perché più persone non usano questo sito. Hanno anche una pagina leggibile dalla macchina in modo da non dover schermare le informazioni.
deft_code

1
Potresti non ottenere l'IP corretto se stai operando all'interno del proxy.
Jack

1
icanhazip.com restituisce l'IP e niente di più. Perfetto per l'incorporamento in uno script bash!
lukecyca

ipinfo.io/ip è un'altra alternativa. Se si arriccia ipinfo.io , restituisce anche il nome host, le informazioni di geolocalizzazione e altro in formato JSON.
Ben Dowling


-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
Questa è una soluzione pesante, solo per Windows (domanda su Linux), mal formattata che si basa su servizi fuori dal controllo locale tramite l'analisi fragile del contenuto del sito web. Non riesco a trovare alcun lato positivo di questa "soluzione".
viraptor

1
Ah! L'analisi HTML cerca anche un commento che sia un avvertimento su ciò che viene fatto esattamente.
notlesh
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.